This post exercises every custom component available in the blog renderer.
All of these can be dropped directly into any .mdx file.
Callout
Four variants — useful for surfacing key information inline.
This is a note — general context or background info.
This is a tip — a shortcut or best practice worth highlighting.
This is a warning — something that could go wrong if you're not careful.
This is important — a critical detail that must not be missed.
You can also trigger callout variants using plain GitHub-style blockquote syntax — no JSX needed:
Works in regular markdown too, no component import required.
CodeFile
Named code blocks — great for tutorial posts where you're showing exactly which file to edit.
import { getAllPostsMeta } from "@/lib/content/blog";
export default function BlogPage() {
const posts = getAllPostsMeta();
return <BlogGrid posts={posts} />;
}
GITHUB_TOKEN=ghp_xxxxxxxxxxxx
NEXT_PUBLIC_SITE_URL=https://yoursite.com
CodeTabs
Multi-tab code blocks — great for showing the same thing across package managers or frameworks.
npm install gray-matter remark-gfm rehype-highlight
Steps
Step-by-step tutorials with a numbered connector line.
Install dependencies
Add the packages needed for the blog feature:
npm install gray-matter next-mdx-remote remark-gfm rehype-highlight
Create your first post
Add an .mdx file to content/blog/. The filename becomes the URL slug.
touch content/blog/my-first-post.mdx
Add frontmatter
Every post needs a title, date, excerpt, and tags:
---
title: "My First Post"
date: "2026-03-08"
excerpt: "A short summary of the post."
tags: ["web", "nextjs"]
---
Start writing
Below the frontmatter, write regular Markdown or JSX — any of the components on this page are available immediately, no imports needed.
Kbd
Inline keyboard shortcuts with keycap styling.
Open the command palette with ⌘K (Mac) or CtrlK (Windows/Linux).
Save a file: ⌘S. Search globally: ⌘ShiftF.
FileTree
Visual directory structure — essential for "here's how the project is laid out" sections.
Timeline
Changelog or history sections with a clean vertical connector.
Blog redesign
Rewrote all in-blog MDX components from scratch. New additions: Timeline, MetricCard, PostImage, and PostShare.
GitHub snapshot
Added a real-time GitHub activity section to the homepage, powered by the GitHub REST and GraphQL APIs.
Travel page
Built an interactive world map with react-simple-maps showing every
country visited.
Initial launch
First version of the portfolio went live with hero, projects grid, and contact form.
MetricCard
Inline stat display — useful for benchmarks, results sections, or project summaries.
Routes
12
SSG + dynamic
Perf score
98
Lighthouse
Build time
3.5s
Turbopack
TS errors
0
strict mode
PostImage
Styled images with optional caption and photo credit.
ToggleList
Collapsible sections for hiding/showing detailed content — great for FAQs, advanced options, or supplementary info.
The ToggleList component is perfect for hiding verbose content behind collapsible headers. Users can expand sections that interest them without being overwhelmed by information.
Wrap multiple ToggleItem components inside ToggleList. Each item has a title prop and accepts any JSX content as children.
<ToggleList>
<ToggleItem title="First item">Content here</ToggleItem>
<ToggleItem title="Second item">More content</ToggleItem>
</ToggleList>
Yes! Add the defaultOpen={true} prop to any ToggleItem to have it expanded by default. Like this one.
Anything! Paragraphs, lists, code blocks, images — all standard Markdown and MDX components work inside ToggleItem children.
- Bullet point one
- Bullet point two
- Bullet point three
Combining components
Components compose naturally. Here's a full mini-tutorial using several at once:
Adding a new page
Follow these steps any time you add a top-level route to the site.
Add the route to site.config.ts
The nav array drives both the desktop header and the mobile sheet automatically.
nav: [
{ label: "Projects", href: "/projects" },
{ label: "Blog", href: "/blog" }, // ← add this
{ label: "Travel", href: "/travel" },
],
Create the page file
Next.js App Router — just drop a page.tsx in the right folder:
mkdir -p app/blog && touch app/blog/page.tsx
Add a command palette entry
Open components/site/command-palette.tsx and add a CommandItem
with the appropriate lucide icon. Press ⌘K to test it.
