A Template System for Research Writing
How I built a reusable architecture for publishing paper breakdowns and blog posts on one site — composable primitives, data-driven layouts, and responsive typography.
- engineering
- design systems
- next.js
I kept rewriting the same layout every time I wanted to publish a paper breakdown. Different fonts, inconsistent spacing, a slightly different navigation each time. Eventually I gave up and built a small template system on top of Next.js — and it made writing so much easier that I want to explain the moving parts.
The goal: one cohesive design language that can render both paper breakdowns and short blog posts, stay pleasant on mobile and desktop, and be easy to extend without touching layout code.
Three layers
Everything in this site is split into three layers that map cleanly onto Next.js conventions:
- Layout: PaperLayout (hero + side nav + column) or PostLayout (centered editorial column). Both share a SiteHeader and SiteFooter.
- Primitives: Section, Prose, Callout, CalloutGrid, KeyConceptGrid, Roadmap, CodeBlock, MathEquation, CTASection, Citation.
- Registry: A single lib/content/registry.ts drives the index pages. To add a paper or post, drop an entry in and create a page file.
Composition over configuration
Instead of a giant config object that a single renderer interprets, each post or paper is just a React component. The PostLayout handles the masthead, the body column, and the footer. Everything inside is composed with primitive components.
Why not MDX?
<Prose> gives me the same typographic defaults as the generated content.A minimal blog post template
Here is the entire shape of a new post. Drop this file into app/blog/your-slug/page.tsx, add an entry to the registry, and you're done — responsive design, tags, reading time and dates are all handled:
// app/blog/your-post/page.tsx
import { PostLayout } from "@/components/layouts"
import { Lede, Prose, Callout, CodeBlock } from "@/components/content"
export default function YourPost() {
return (
<PostLayout
title="Your title here"
description="A short subtitle."
date="2026-04-16"
readingMinutes={5}
tags={["engineering"]}
>
<Lede>Opening paragraph...</Lede>
<Callout title="A point worth highlighting">
Any ReactNode content goes here.
</Callout>
<CodeBlock language="python" code={"print('hello')"} />
</PostLayout>
)
}Responsive by default
Both layouts start from a mobile-first column and add breakpoints as they grow:
- Paper: Side navigation dots collapse into a bottom-right hamburger below lg; content column centers itself on mobile and shifts right of the rail on desktop.
- Post: Single 2xl-max column with generous line-height; masthead adapts to any title length via text-balance.
- Cards: Content grids use one column on phones, two from lg, and keep consistent gap spacing.
Where next
The template will keep evolving. If you want to read actual content built on top of it, try the two papers: Topos-Theoretic Probability and Categorical Probability. Both render through the exact same primitives as this post.