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
- Read the file. The Next.js App Router's catch-all route at
(case-studies)/[...slug]/page.tsxresolves the requested URL to a file undermdx-content. - Parse metadata.
fetchers.tsreads the file and usesgray-matterto separate frontmatter, such as the page title, from the body. It also extracts Markdown headings for the table of contents. - Build navigation.
sidebar-data.tsscans content folders to build sidebar sections and links. The page builds breadcrumbs from the URL and usesfetchers.tsto find previous and next entries. - Compile and render the content. The route imports the matching
.mdxfile as a React component.@next/mdxcompiles its Markdown, JSX, and component imports, withremark-gfmfor GitHub-flavored Markdown andremark-frontmatterto 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>
Navigation and Organization
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.