How to write CLAUDE.md: the three levels (user, project, directory) and a ready-to-use template
Where Claude Code looks for CLAUDE.md, which level wins, what belongs in it and what does not, plus a template you can drop into a small or medium web project today.
Contents
If you keep repeating the same instructions to Claude Code, or the coding conventions you asked for are ignored again, the fix is usually a better CLAUDE.md.
CLAUDE.md is a Markdown file Claude Code loads automatically at the start of every session. This article sorts out what to put where across three levels, and gives you a template to start from.
KEY POINT
What you will learn
- The places CLAUDE.md can live and when each is loaded
- What is worth writing and what has little effect
- A template for small and medium web projects
The three levels
| Level | Path | Purpose | In Git |
|---|---|---|---|
| User | ~/.claude/CLAUDE.md | Preferences shared by all your projects (language, tone, frequent commands) | No |
| Project | ./CLAUDE.md or ./.claude/CLAUDE.md | Team conventions, build steps, structure | Yes |
| Personal (per project) | ./CLAUDE.local.md | Your own notes and local environment details | No (add to .gitignore) |
There is a fourth option: a CLAUDE.md inside a subdirectory. A file at packages/api/CLAUDE.md is loaded when Claude touches files in that directory. In a monorepo, put the overall policy at the root and package-specific rules in each package.
Run /memory to see which files are loaded. When you are not sure why an instruction is or is not in effect, look there first.
Generate the first draft with /init
Run /init at the repository root and Claude inspects the project and writes a draft. Do not keep it as is. Cut:
- Anything you can learn from the README (a long description of the project's purpose)
- Anything Claude can discover by looking (directory listings)
Keep only the agreements you cannot see by reading the code.
用語解説
Import syntax: A line such as @docs/coding-style.md pulls that file into CLAUDE.md. Use it to keep long conventions in separate files.
What to write
Frequently used commands
Prevents Claude from guessing and running the wrong command.
## Commands
- Tests: `npm test` (unit) / `npm run test:e2e` (E2E, needs Docker running)
- Lint: `npm run lint` — must pass before committing
- Build: `npm run build`
- DB migrations: `npm run db:migrate`
Conventions to follow
A short "why" helps Claude act in the spirit of the rule when it hits a case you did not anticipate.
## Conventions
- Every new function gets a JSDoc comment (API docs are generated from types)
- Never use `any`; use `unknown` and narrow it
- Dates go through `date-fns`; do not add `moment` (bundle size)
Places not to touch
## Do not modify
- `src/generated/` (generated code; edit `schema/` instead)
- Existing files under `migrations/` (add new files instead)
How to work
## Workflow
- For changes touching 3+ files, outline the plan as bullet points before editing
- Never report "done" while tests are failing
- One logical change per commit
What not to write
| Content | Why |
|---|---|
| Directory trees or file listings | Claude explores on its own; the list goes stale and becomes wrong |
| Generic best practices ("write readable code") | Vague instructions do not change behavior |
| Secrets, internal URLs, credentials | CLAUDE.md is committed. Do not put secrets in CLAUDE.local.md either; use environment variables |
| Huge specifications | They cost context every session. Import only the parts you need with @ |
CLAUDE.md costs context on every session
The whole file is sent at the start of each session. Keeping a project CLAUDE.md around 100–200 lines balances the strength of the instructions against token usage. See Managing context in Claude Code.
Template for a small or medium web project
# Project overview
Next.js + TypeScript admin dashboard. API lives in `apps/api` (NestJS).
## Commands
- Dev server: `pnpm dev`
- Tests: `pnpm test` (always run the tests related to files you changed)
- Lint / format: `pnpm lint && pnpm format`
- Type check: `pnpm typecheck`
## Conventions
- Function components + hooks only; no class components
- Styling with Tailwind. CSS Modules exist only for legacy code
- API response types live in `packages/types` and are shared by front and back end
- Do not swallow errors; throw `AppError`
## Do not modify
- `packages/types/generated/` (generated from OpenAPI)
- `.github/workflows/` (propose changes instead)
## Workflow
- Read the related tests before changing code
- For changes touching 3+ files, present the plan first
- Include the test command you ran and its result in the completion report
## References
@docs/architecture.md
Grow it during sessions
Type a line beginning with # during a conversation and Claude offers to save it to CLAUDE.md. Telling Claude "from now on, do X" out loud does not survive to the next session; anything you find yourself repeating should be written down with #, and the file grows naturally.
For sharing the same content with Codex's AGENTS.md and Gemini CLI's GEMINI.md, see the Japanese article on context-file sharing; an English version will follow.
Summary
- Place CLAUDE.md at the user, project and personal levels and check what is loaded with
/memory - Write only agreements you cannot see in the code: commands, conventions, no-go areas and workflow
- Leave out directory trees and generic advice; import long documents with
@ - Add to it during sessions with
#
FAQ
- Where should CLAUDE.md go?
- Rules for all your projects go in ~/.claude/CLAUDE.md, shared project rules in CLAUDE.md at the repository root, and personal notes in CLAUDE.local.md.
- What if CLAUDE.md gets too long?
- Split it with the import syntax, for example @docs/architecture.md. A CLAUDE.md placed in a subdirectory is only loaded when Claude works in that directory.
- Is CLAUDE.md the same thing as .cursorrules or AGENTS.md?
- They play the same role of briefing the agent, but each product reads its own file. A separate article covers how to share content between them.
Questions & answers
Q. CLAUDE.md is injected into the system prompt at session start, so why is it not applied before the first answer?
The premise doesn't match the documentation: CLAUDE.md is not in the system prompt. The memory documentation states it directly:
CLAUDE.md content is delivered as a user message after the system prompt, not as part of the system prompt itself. Claude reads it and tries to follow it, but there's no guarantee of strict compliance, especially for vague or conflicting instructions.
So the expectation that it must be applied because it is in the system prompt doesn't hold. You can confirm the file loaded by running /context and checking the list under Memory files — if it is listed, Claude can see it.
For better adherence, the docs name three things:
- Make instructions concrete enough to verify. "Use 2-space indentation" rather than "format code properly"
- Look for conflicting instructions across your CLAUDE.md files, nested CLAUDE.md files in subdirectories, and
.claude/rules/. When two rules contradict each other, Claude may pick one arbitrarily - Keep each file under 200 lines. Longer files consume more context and reduce adherence, per the docs
For an instruction that must run at a specific point — before every commit, after each file edit — write it as a hook instead of prose. Hooks execute as shell commands at fixed lifecycle events and apply regardless of what Claude decides to do.
If you want instructions at the system prompt level, pass --append-system-prompt at launch. It is better suited to scripts and automation than to interactive use.
Q. My CLAUDE.md lists 11 mandatory project rules, but they are ignored across sessions. Can CLAUDE.md rules be enforced?
CLAUDE.md shapes what Claude tries to do. It is not the mechanism that decides what is allowed. The official permissions documentation states this directly:
Permission rules are enforced by Claude Code, not by the model. Instructions in your prompt or
CLAUDE.mdshape what Claude tries to do, but they don't change what Claude Code allows.
So a rule like "nothing executes without an explicit go-ahead in this session" is not guaranteed by writing it in CLAUDE.md. The file is loaded, but following it is left to the model's judgement. The same page names the four things that do enforce: /permissions, permission rules, a permission mode, and a PreToolUse hook.
For anything you genuinely need blocked, write it in permissions in settings.json. Rules are evaluated deny → ask → allow, and the first match in that order determines the outcome. Specificity does not change the order, so a broad deny beats a narrower allow.
{
"permissions": {
"ask": [
"Bash",
"Edit"
],
"deny": [
"Bash(rm -rf *)"
]
}
}
For conditions that rules cannot express, use a PreToolUse hook. Per the docs, PreToolUse hooks run before the permission prompt on every tool call, and their output can deny the call, force a prompt, or skip the prompt. A hook that exits with code 2 stops the call before permission rules are even evaluated.
Hook decisions do not bypass permission rules, though: deny and ask rules are evaluated regardless of what the hook returns, so a matching deny still blocks the call and a matching ask still prompts even when the hook returned "allow".
On the cross-session part: CLAUDE.md is loaded every session, but how the model treats a summary of earlier context is its own judgement. If that behavior has to be fixed, pin it with permissions or a hook rather than with prose.
For the full setup, see Configuring permissions in Claude Code's settings.json.
Primary sources
This article was drafted by AI from official documentation and reviewed by the site operator before publishing. Found a mistake? Let us know via the contact page.