Stop copy-pasting code into documentation. Keep your markdown examples automatically synchronized with real source files.
Your documentation has code examples, but they get outdated:
- You update your source code but forget to update the docs
- Code examples in README/docs become stale and misleading
- Copy-paste errors introduce bugs in documentation
- Code examples can't be validated, linted, or tested like real source code
- Maintaining multiple copies of the same code is painful
markdown-code keeps your documentation in sync automatically:
- Single source of truth: Code examples come from real files
- Always accurate: Documentation updates when code changes
- Validated code: Snippets can be linted, type-checked, and tested like any source file
- Zero maintenance: Sync happens automatically in CI/CD
- Extract existing: Migrate current docs with one command
```js
function greet(name) {
return 'Hello ' + name; // Oops, code changed but docs didn't!
}
``````js snippet=src/utils/greet.js
function greet(name) {
return `Hello, ${name}! Welcome to our app.`; // Always current!
}
```Result: Your documentation stays accurate as your code evolves.
Install the markdown-code skill to give your AI coding agent knowledge of this tool's workflows and commands:
npx skills add scalvert/markdown-codeWorks with any agent that supports the skills ecosystem.
No installation required - use it directly:
npx markdown-code --help
npx markdown-code init --extractOr install once, then use the shorter md-code command:
npm install -g markdown-codeNow you can use the convenient md-code binary:
md-code --help
md-code init --extractNote: All examples in this README show both
npx markdown-codeandmd-codevariants.
Use the snippet= directive in your fenced code blocks:
```ts snippet=path/to/file.ts
// This content will be replaced
```
```ts snippet=path/to/file.ts#L10-L20
// This will include lines 10-20 only
```
```ts snippet=path/to/file.ts#L5
// This will include only line 5
```
```ts snippet=path/to/file.ts#L5-
// This will include from line 5 to end of file
```A line range that selects no lines, for example one that starts past the end of the file, is reported as an empty-range error and the code block is left unchanged.
Quote a path that contains spaces: snippet="my docs/file.ts". The directive is
read the way Markdown reads the rest of the fence line, so backslash escapes and
character references apply: snippet=a\_b.ts refers to a_b.ts, and
snippet=a&b.ts to a&b.ts. When extract writes a directive, it escapes
the path so it reads back unchanged.
markdown-code provides two powerful workflows for keeping your documentation in sync with your code:
- Extract from existing docs - Convert unmanaged code blocks into manageable snippets
- Reference source files - Keep documentation in sync with your actual codebase
Perfect for projects with existing markdown documentation that contains code blocks.
Use this when:
- You have markdown files with unmanaged code blocks
- You want to extract code examples into separate files
- You need to start managing existing documentation
How it works:
- Run
npx markdown-code check(ormd-code check) to discover manageable code blocks - Run
npx markdown-code init --extract(ormd-code init --extract) to extract them into snippet files - Your markdown is updated to reference the new snippet files
- Future changes to snippets automatically sync to documentation
Perfect for keeping documentation in sync with your actual project source code.
Use this when:
- You want documentation to reference your actual source files
- You need line-range extraction from existing code
- You want living documentation that stays current with code changes
How it works:
- Add snippet directives to reference source files with line ranges
- Run
npx markdown-code sync(ormd-code sync) to populate markdown with current source content - When source files change,
npx markdown-code check(ormd-code check) detects drift - Run
npx markdown-code sync(ormd-code sync) to update documentation with latest changes
.mdx files (Docusaurus, Nextra, etc.) are fully supported. Point the glob at
them:
{
"markdownGlob": "docs/**/*.{md,mdx}"
}MDX files are parsed with remark-mdx + remark-frontmatter, so fences nested
inside JSX components are found, JSX indentation never produces phantom code
blocks, and {...} inside YAML frontmatter parses cleanly. Snippet directives
coexist with other fence metadata, such as title="app.ts" and snippet=app.ts.
Three guarantees worth knowing:
- Non-destructive extraction:
extractonly insertssnippet=<ref>into opening fence lines (offset-addressed, never regex-matched) — stripping the annotations reproduces your original file byte-for-byte. - Fail-safe parsing: a file that isn't valid MDX (e.g. HTML
<!-- -->comments, which MDX forbids) is reported and left untouched. - Snippets stay inside their block: if a snippet contains a fence line
such as
```,synclengthens that block's fence (to````and so on) so the snippet can't close the block early. Fences are never shortened.
Note: framework-specific MDX extensions (like Docusaurus {#heading-id}
anchors) are not part of the MDX grammar and will be reported as parse errors;
those files are skipped safely.
Snippet directives may reference HTTPS URLs. The URL can include the same line-range syntax as a local path:
```ts snippet=https://raw.githubusercontent.com/example/project/main/app.ts#L10-L20
```HTTPS is allowed by default. Plain HTTP is rejected unless allowInsecureHttp is
set to true, including an HTTPS URL that redirects to HTTP. Remote files larger
than 5 MB are rejected. Remote requests time out after 30 seconds by default; configure
remoteTimeout in milliseconds when needed. Remote failures remain errors even
when missingSnippetSeverity is set to warning, because the source was
configured but could not be loaded.
- Automatic Sync: Replace fenced code blocks with contents from real files
- MDX Support: Parse
.mdxfiles, including fences nested in JSX - Extract Mode: Create snippet files from existing code blocks in markdown
- Line Range Support: Extract specific line ranges using
#Lx-Lysyntax - Check Mode: Verify documentation is in sync without making changes
- Multi-language: Support for any programming language
- Configurable: Flexible configuration via
.markdown-coderc.json
| Command | Description | Example |
|---|---|---|
npx markdown-code / md-code |
Update markdown with snippet content (default) | npx markdown-code or md-code |
npx markdown-code sync / md-code sync |
Same as above, explicit | npx markdown-code sync or md-code sync |
npx markdown-code check / md-code check |
Verify files are in sync (CI-friendly) | npx markdown-code check or md-code check |
npx markdown-code init / md-code init |
Create config and snippets directory | npx markdown-code init or md-code init |
npx markdown-code extract / md-code extract |
Extract code blocks to snippet files | npx markdown-code extract or md-code extract |
npx markdown-code init --extract / md-code init --extract |
Setup + extract in one step | npx markdown-code init --extract or md-code init --extract |
npx markdown-code eject / md-code eject |
Remove snippet directives, snippets, and config | npx markdown-code eject or md-code eject |
| Option | Description | Example |
|---|---|---|
--config |
Custom configuration file | npx markdown-code --config custom.json or md-code --config custom.json |
--snippet-root |
Override snippet directory | npx markdown-code --snippet-root ./src or md-code --snippet-root ./src |
--markdown-glob |
Override markdown file pattern | npx markdown-code --markdown-glob "docs/**/*.md" or md-code --markdown-glob "docs/**/*.md" |
--exclude-glob |
Override exclusion patterns | npx markdown-code --exclude-glob "node_modules/**,dist/**" or md-code --exclude-glob "node_modules/**,dist/**" |
--include-extensions |
Override file extensions | npx markdown-code --include-extensions .ts,.js,.py or md-code --include-extensions .ts,.js,.py |
--missing-snippet-severity |
Missing snippet severity (error or warning) |
npx markdown-code check --missing-snippet-severity warning |
Create a .markdown-coderc.json file in your project root:
{
"snippetRoot": "./snippets",
"markdownGlob": "**/*.md",
"excludeGlob": [
"node_modules/**",
".git/**",
"dist/**",
"build/**",
"coverage/**",
".next/**",
".nuxt/**",
"out/**",
"target/**",
"vendor/**"
],
"includeExtensions": [
".ts",
".js",
".py",
".java",
".cpp",
".c",
".go",
".rs",
".php",
".rb",
".swift",
".kt"
],
"missingSnippetSeverity": "error"
}| Option | Description | Default |
|---|---|---|
| snippetRoot | Base directory for resolving snippet paths | "." |
| markdownGlob | Glob pattern to find Markdown files | "**/*.md" |
| excludeGlob | Array of glob patterns to exclude from processing | Common build/dependency directories |
| includeExtensions | File extensions to consider for snippets and extraction | [".ts", ".js", ".tsx", ".jsx", ".py", ".rb", ".go", ".rs", ".java", ".cpp", ".c", ".cs", ".php", ".sh", ".bash", ".zsh", ".fish", ".json", ".yaml", ".yml", ".toml", ".xml", ".html", ".css", ".scss", ".less", ".sql", ".md", ".txt"] |
| missingSnippetSeverity | Severity for missing local snippet files (error or warning) |
"error" |
On Windows, \ in markdownGlob and excludeGlob is treated as a path
separator, so docs\**\*.md works. To match a literal glob character such as
[ or ( there, use a bracket expression (docs/[[]draft].md) instead of a
backslash escape.
eject is destructive: it removes snippet directives, deletes the configured
snippet directory, and removes the configuration file. It asks for confirmation
before proceeding and refuses to delete the working directory itself. Ensure the
snippet root is a dedicated child directory before confirming.
See CONTRIBUTING.md for development setup and contribution guidelines.
MIT License - see the LICENSE file for details.
