The starter template gets you going, but a real CLAUDE.md earns its value by encoding decisions that would otherwise live only in your head. This pattern is based on the file that powers this site — a Next.js blog with static generation, a tag system, an AI toolkit section, CI/CD, and tests.
What makes it work: Claude Code reads CLAUDE.md on every invocation. The more precisely you describe your architecture, the fewer wrong assumptions it makes. The sections below aren't arbitrary — each one prevents a specific class of mistake.
The pattern
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Commands
- `npm run dev` - Start development server
- `npm run build` - Build for production (runs next-sitemap as postbuild)
- `npm run start` - Start production server
- `npm test` - Run tests once
- `npm run test:watch` - Run tests in watch mode
- `npm run lint` - Run ESLint
## Architecture
This is a Next.js blog using the Pages Router with static generation. Blog posts are written in Markdown and converted to HTML at build time.
### Key Directories
- `_posts/` - Markdown blog posts with frontmatter (title, date, author, tags, description)
- `pages/` - Next.js pages using Pages Router (not App Router)
- `components/` - React components
- `lib/` - Data fetching and utilities
### Data Flow
1. `lib/api.js` reads Markdown files from `_posts/` using `gray-matter` for frontmatter parsing
2. `lib/markdownToHtml.js` converts Markdown content to HTML using `remark` and `remark-html`
3. Pages use `getStaticProps` and `getStaticPaths` for static generation
### Frontmatter Schema
```yaml
title: "Post title"
date: "2017-12-21T11:30:00.169Z"
author:
name: "Author Name"
tags: ["tag1", "tag2"]
description: "Meta description for SEO"
Testing
Unit tests use Vitest and are located in __tests__/lib/.
Test fixtures in __tests__/fixtures/_posts/ provide sample markdown files for testing without touching production content.
CI/CD
GitHub Actions runs on all PRs and pushes to main:
- ESLint
- Vitest tests
- Production build
Tech Stack
- Next.js 16 with Pages Router
- React 19
- Tailwind CSS 3 with Typography plugin
- Vitest for testing
- ESLint with next/core-web-vitals
## Why each section matters
**Commands** — Without this, Claude guesses your task runner or invents scripts that don't exist. List every command you actually use, including postbuild hooks.
**Architecture** — The single most important section. "Pages Router" prevents Claude from generating App Router code. "Static generation" prevents it from adding API routes for data that should be fetched at build time. Be explicit about what your project is *not*.
**Key Directories** — Claude needs to know where to look. Without this, it searches the entire repo or guesses wrong paths.
**Data Flow** — Describes how data moves from source (markdown files) through processing (gray-matter, remark) to output (static pages). This prevents Claude from inventing alternative pipelines.
**Frontmatter Schema** — Without a concrete example, Claude will guess field names and types. Show the exact YAML shape.
**Testing** — Tell Claude where tests live, what framework you use, and where fixtures are. This prevents it from creating tests in the wrong location or using the wrong assertion library.
**CI/CD** — Knowing what CI checks exist prevents Claude from breaking the build. If lint runs in CI, Claude knows it can't ignore lint errors.
**Tech Stack** — Version numbers matter. "React 19" means different lint rules than React 18. "Pages Router" means different patterns than App Router.