A Claude Code skill is a markdown file that Claude reads and executes as a precise, repeatable instruction set. Skills are what make the difference between asking Claude to "add a feature" and getting inconsistent results, versus running /feature-module task and getting 7 correctly structured files every time.
This post covers how to write skill files that Claude actually follows -- the structure, the step patterns, and the small details that prevent drift.
A skill is a .md file in .claude/skills/ (or .claude/commands/). When you type /skill-name in Claude Code, Claude reads the file and executes its instructions.
The difference between a skill and a regular prompt:
If you find yourself repeating the same multi-step prompt more than twice, write a skill.
.claude/
skills/
feature-module/
SKILL.md <-- full skill instructions
nextjs-api-route/
SKILL.md
commands/
generate-post.md <-- simpler single-file format
Both locations work. Use skills/<name>/SKILL.md for complex skills with supporting files. Use commands/<name>.md for shorter, self-contained skills.
The filename (without extension) becomes the slash command. feature-module/SKILL.md is invoked with /feature-module.
Skills in .claude/skills/<name>/SKILL.md use YAML frontmatter followed by markdown content:
Frontmatter (required):
---
name: feature-module
description: One sentence describing what this skill does and when Claude should trigger it automatically.
---
Title -- a short H1 after the frontmatter:
# Feature Module Skill
Steps -- the core of the skill, numbered and explicit:
modules/ to understand the structure.modules/<name>/<name>.schema.ts with a pgTable definition.Usage -- shows Claude the expected argument format:
/feature-module <name>
Example:
/feature-module categories
The simpler .claude/commands/<name>.md format skips frontmatter entirely. The first line is the description, followed directly by a steps section. Use it for short, self-contained skills.
Use the skills/ format when you want Claude to auto-trigger the skill from context -- the description frontmatter field is what Claude matches against.
The quality of a skill comes down to how you write its steps. These patterns work:
Be explicit about file paths. Vague instructions drift.
Bad:
Create the schema file for the module.
Good:
Create modules/<name>/<name>.schema.ts with a pgTable definition.
Table name: plural snake_case. Export name: camelCase + Table suffix (e.g. taskTable).
Specify what to read before writing. If the skill depends on existing conventions, tell Claude to read them.
db/drizzle.ts to understand how schemas and relations are registered.modules/post/) to understand the 7-file structure.List every file to create. Do not say "scaffold the module" -- say exactly which files.
Create these 7 files:
modules/<name>/<name>.schema.tsmodules/<name>/<name>.relations.tsmodules/<name>/<name>.types.tsmodules/<name>/<name>.validation.tsmodules/<name>/<name>.repo.tsmodules/<name>/<name>.service.tsmodules/<name>/index.tsInclude the side effects. If creating a file requires updating another file, say so explicitly.
After creating the schema, add an export to db/schema.ts:
export * from '../modules/<name>/<name>.schema';
After creating the relations file, import and spread it into the db client in db/drizzle.ts.
Use <name> as a placeholder. Claude replaces it with the argument from the slash command. This is the standard convention across all boilerplate skills.
Skills can branch based on what Claude finds:
modules/<name>/ already exists.
/nextjs-api-route GET /api/tasks).
Claude handles this branching in natural language -- no if/else syntax needed.
Skills can instruct Claude to read files before acting:
CLAUDE.md and find the "Blog Style Guide" section. Use the voice, keywords, and stop words defined there when generating the post..env and extract AI_API_KEY (do not hardcode the value).http://localhost:3000/api/posts to get existing post slugs. Do not create a post whose title overlaps with an existing one.This is how the generate-post skill works: it reads style conventions, checks for overlap, then creates the post -- all in one invocation.
If a skill generates content that will be stored in a database or sent over an API, add an explicit ASCII constraint:
--, smart quotes with straight quotes ("/').....Without this, Claude occasionally outputs Unicode punctuation that breaks JSON serialization or causes display issues in terminals.
Always include a Usage section at the bottom of the skill. It shows Claude the expected argument format:
Single argument:
/feature-module <name>
Example:
/feature-module categories
Multiple argument patterns:
/nextjs-api-route <METHOD> <path>
/nextjs-api-route <path> (generates GET + POST)
Examples:
/nextjs-api-route POST /api/tasks
/nextjs-api-route /api/tasks/:id
Here is a complete skill that scaffolds a new React component. Create it at .claude/skills/new-component/SKILL.md:
Frontmatter:
---
name: new-component
description: Scaffold a new React component following the project's UI conventions.
---
Title + steps:
Title: # New Component Skill
Steps:
components/ to understand the structure.components/<name>/<name>.tsx:
interface <Name>Props { ... }cn() from lib/utils for classname merging"use client" only if the component needs event handlers or hookscomponents/<name>/<SubName>.tsxcomponents/ui/ -- those are shadcn components.Usage section:
## Usage
/new-component <name>
Example:
/new-component PricingCard
Skills and CLAUDE.md complement each other. CLAUDE.md defines your conventions globally -- naming rules, architecture patterns, anti-patterns. Skills reference those conventions rather than repeating them.
For example, a skill step can say: "Follow the naming conventions defined in CLAUDE.md" -- then list the specific rules:
tasks, users)snake_casecamelCase + Table suffixThis keeps skills short and ensures they stay in sync with your conventions automatically. If you update a convention in CLAUDE.md, every skill that references it picks up the change.
For a full guide on writing CLAUDE.md, see "How to configure CLAUDE.md for a Next.js project."
A good skill is explicit about paths, files, and side effects. It tells Claude what to read before writing. It uses <name> as a placeholder and includes a Usage section with examples. Write one any time you repeat a multi-step prompt more than twice -- the investment pays back immediately.