← Back to Blog

The Stack Behind This Blog

|6 min read|

A walkthrough of the blog engine powering datagobes.dev — and a living test of every MDX feature it supports.

This post serves double duty. It's a genuine look at how this blog is built, and it exercises every part of the rendering pipeline. Every heading, code block, table, equation, and component on this page is proof that the system works. If you're reading this, the pipeline held.

The Rendering Pipeline#

The blog is server-rendered MDX, stored in Supabase and compiled at build time via Next.js ISR. Posts are written in MDX — standard Markdown plus the ability to embed React components directly in the content.

Here's the full pipeline, rendered by a custom <Pipeline /> component — itself proof that arbitrary React components work inside posts:

Rendering Pipeline

Supabase

PostgreSQL — posts table with MDX content, tags, metadata

evaluate()

next-mdx-remote-client/rsc — server-side MDX to React

Remark

Markdown AST transforms

remark-gfmremark-math

Rehype

HTML AST transforms

rehype-slugrehype-autolink-headingsrehype-pretty-code + Shikirehype-katex

Component Map

23 styled elements + custom MDX components

CalloutArtifactEmbedPipeline

Static HTML

ISR — revalidates every hour, instant on publish

Every post flows through this chain. No client-side JavaScript is needed for rendering — it's all server-side React. The component you're looking at right now was compiled on the server and shipped as static HTML.

Syntax Highlighting#

Code blocks get full syntax highlighting via Shiki and rehype-pretty-code, running at build time with the github-dark-dimmed theme. Zero runtime JS — just pre-colored HTML.

import { createClient } from "@supabase/supabase-js";
 
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY;
 
export const supabase = createClient(supabaseUrl!, supabaseKey!);

And Python, because this blog covers more than TypeScript:

from dataclasses import dataclass
 
@dataclass
class Post:
    title: str
    slug: str
    content: str
    status: str = "draft"
 
    @property
    def is_published(self) -> bool:
        return self.status == "published"

Every code block gets a copy button that appears on hover. Try it.

Inline code also works: npm run build, revalidate = 3600, SELECT * FROM posts.

Math & LaTeX#

The pipeline includes remark-math and rehype-katex for native math rendering. Inline math like E=mc2E = mc^2 works naturally in prose, and block equations get their own display:

E=ρε0\nabla \cdot \mathbf{E} = \frac{\rho}{\varepsilon_0} ex2dx=π\int_{-\infty}^{\infty} e^{-x^2} dx = \sqrt{\pi}

This is server-rendered KaTeX — the equations are compiled to HTML at build time, no client-side math rendering.

GFM Features#

GitHub Flavored Markdown via remark-gfm gives us tables, task lists, strikethrough, and footnotes.

Tables#

LayerTechnologyPurpose
DatabaseSupabase (PostgreSQL)Post storage, tags as JSON arrays
Renderingnext-mdx-remote-clientServer-side MDX to React
HighlightingShiki + rehype-pretty-codeBuild-time syntax coloring
Mathremark-math + rehype-katexLaTeX equations
FrameworkNext.js 16 (App Router)ISR, static generation, routing
HostingVercelEdge deployment, passkey auth

Task Lists#

What's been built so far:

Strikethrough#

The blog uses client-side highlighting runs Shiki at build time for zero-JS syntax coloring.

Custom Components#

MDX lets us register React components that authors can use directly in post content. The component map currently includes 23 styled HTML elements plus these custom components:

i

This is an info callout. Use it for supplementary context, tips, or background information.

!

This is a warning callout. Common pitfalls, breaking changes, or non-obvious requirements.

x

This is a danger callout. Things that can break your setup, lose data, or cause security issues.

Embedded Artifacts#

The <ArtifactEmbed> component renders any artifact from the portfolio inline in a post. Here's the regex playground, running inside this page:

Loading artifact: regex-playground...

The artifact loads in a sandboxed iframe — fully interactive, with its own styles and state, isolated from the blog's rendering. The loading spinner fades out when the iframe is ready.

Heading Anchors & Table of Contents#

Every h2 and h3 on this page has an anchor link. Hover over any heading to see the # link appear. Click it to get a shareable URL that scrolls directly to that section.

On wide screens, the table of contents on the right tracks your scroll position and highlights the current section. It uses an IntersectionObserver to detect which heading is visible and updates in real time.

Internal links use Next.js <Link> for client-side navigation: back to the blog, artifacts gallery, about page.

External links open in a new tab with a small arrow indicator: Supabase docs, Next.js, Shiki.

Tags#

Every post can have tags — stored as a JSON array in Supabase, rendered as clickable pills below the title. Click any tag to see all posts with that tag. The tag pages use the same card grid as the main blog listing.

The Admin#

Posts are written in a custom admin UI at /admin, protected by WebAuthn passkey authentication. The editor includes:

The preview uses the same component map and plugin stack as production, so what you see in the editor is what you get on the live site.

Blockquotes#

The best time to build is now, with the best tools available today. Waiting for the "stable" platform is a form of procrastination.

Blockquotes get the ember left border treatment — subtle but consistent with the site's visual identity.

What's Next#

The blog engine keeps evolving. Upcoming: search, draft preview with shareable links, and eventually interactive AI components embedded in posts. Each phase ships working software — no bells and whistles until the core is solid.


Built with Next.js 16, Supabase, MDX, and a lot of conversations with Claude.