About This Site

This portfolio grew out of a documentation-site generator I built with Next.js and MDX. I adapted its file-based content structure so I could write project case studies and experience entries without creating a separate page component for each one.

The current site combines a custom React landing page with a shared content layout. Case studies live in the repository, alongside the code, and their folder structure drives the content sidebar and page URLs.

How Content Becomes a Page

  1. Read the file. The Next.js App Router's catch-all route at (case-studies)/[...slug]/page.tsx resolves the requested URL to a file under mdx-content.
  2. Parse metadata. fetchers.ts reads the file and uses gray-matter to separate frontmatter, such as the page title, from the body. It also extracts Markdown headings for the table of contents.
  3. Build navigation. sidebar-data.ts scans content folders to build sidebar sections and links. The page builds breadcrumbs from the URL and uses fetchers.ts to find previous and next entries.
  4. Compile and render the content. The route imports the matching .mdx file as a React component. @next/mdx compiles its Markdown, JSX, and component imports, with remark-gfm for GitHub-flavored Markdown and remark-frontmatter to recognize the metadata block. GitHub Markdown CSS supplies the article styling, while shared heading components provide anchors for the table of contents.

Content files support JSX expressions and React component imports alongside Markdown. The Moment Energy case study uses this to embed reusable callout cards and interactive telemetry visualizations directly in the article. Imported client components handle interactivity, while JSX follows React's attribute conventions, such as className and object-valued style props.

For example, a content file can use an existing UI component:

import { Button } from "@/components/ui/button";

<Button asChild variant="outline">
  <a href="#navigation-and-organization">Explore the navigation</a>
</Button>

Each top-level content folder becomes a sidebar section, with links named from a file's frontmatter title or its filename. Adding a content file lets the existing route and sidebar pick it up without adding a dedicated page component or manually editing the sidebar.

The main navbar is configured separately; its links are not generated from the content folders.

Within each article, the shared layout provides:

  • Breadcrumbs based on the current route.
  • An "On this page" table of contents built from Markdown headings.
  • Previous and next links based on the discovered content files.

Most of this behavior lives in fetchers.ts, sidebar-data.ts, and (case-studies)/[...slug]/page.tsx.

Folder Structure Example

src/
  app/
    (case-studies)/
      layout.tsx
      [...slug]/
        page.tsx
    content/
      fetchers.ts
      sidebar-data.ts
      mdx-content/
        about-this-site/
          dynamic-generation.mdx
        experience/
          bcchr.mdx
          moment-energy.mdx
        projects/
          auto-media-publisher.mdx

For example, experience/moment-energy.mdx is served at /experience/moment-energy. Its frontmatter controls the title used in the sidebar and current-page breadcrumb:

---
title: Moment Energy
---

This structure keeps writing and presentation separate: I can update a case study in one content file while reusing the same navigation, typography, and layout across the site.

Libraries and Resources