
Mkdocs
- 128 installs
- 6 repo stars
- Updated July 22, 2026
- julianobarbosa/claude-code-skills
Scaffold, configure, and maintain MkDocs sites with themes, nav, plugins, and CI publishing for developer or product documentation.
About
Helps teams stand up MkDocs documentation sites: project layout, mkdocs.yml, themes like Material, plugins, search, versioning patterns, and deployment steps so SaaS and API products ship readable docs.
- mkdocs.yml configuration help
- Material theme and plugin setup
- Navigation and content structure
- Markdown authoring patterns
- Static site publish workflows
Mkdocs by the numbers
- 128 all-time installs (skills.sh)
- +2 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #606 of 1,879 Documentation skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/julianobarbosa/claude-code-skills --skill mkdocsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 128 |
|---|---|
| repo stars | ★ 6 |
| Last updated | July 22, 2026 |
| Repository | julianobarbosa/claude-code-skills ↗ |
What it does
Scaffold, configure, and maintain MkDocs sites with themes, nav, plugins, and CI publishing for developer or product documentation.
Files
MkDocs Documentation Site Generator
MkDocs is a fast, simple static site generator for building project documentation from Markdown files. Configuration uses a single YAML file (mkdocs.yml).
Quick Start
Installation
# Install MkDocs
pip install mkdocs
# Verify installation
mkdocs --versionCreate New Project
# Create project structure
mkdocs new my-project
cd my-project
# Start development server
mkdocs serveProject Structure Created:
my-project/
├── mkdocs.yml # Configuration file
└── docs/
└── index.md # HomepageMinimal Configuration
# mkdocs.yml
site_name: My Project
site_url: https://example.com/
nav:
- Home: index.md
- About: about.mdCore Commands
| Command | Purpose |
|---|---|
mkdocs new PROJECT | Create new project |
mkdocs serve | Start dev server (localhost:8000) |
mkdocs build | Build static site to site/ |
mkdocs gh-deploy | Deploy to GitHub Pages |
mkdocs get-deps | Show required packages |
Common Options:
-f, --config-file FILE- Use custom config file-s, --strict- Fail on warnings-d, --site-dir DIR- Custom output directory--dirty- Only rebuild changed files--clean- Clean output before build
Project Structure
project/
├── mkdocs.yml # Configuration (required)
├── docs/
│ ├── index.md # Homepage
│ ├── about.md # Additional pages
│ ├── user-guide/
│ │ ├── index.md # Section homepage
│ │ ├── getting-started.md
│ │ └── configuration.md
│ ├── img/ # Images
│ │ └── logo.png
│ └── css/ # Custom CSS
│ └── extra.css
└── custom_theme/ # Theme customizations (optional)
└── main.htmlNavigation Configuration
# Automatic navigation (alphabetically sorted)
# Omit nav key to auto-generate
# Explicit navigation with sections
nav:
- Home: index.md
- User Guide:
- Getting Started: user-guide/getting-started.md
- Configuration: user-guide/configuration.md
- API Reference: api/
- External Link: https://example.com/Writing Documentation
Internal Links
# Link to another page
[See Configuration](configuration.md)
# Link to page in another directory
[Installation](../getting-started/installation.md)
# Link to section anchor
[See Options](configuration.md#options)Page Metadata
---
title: Custom Page Title
description: Page description for SEO
authors:
- John Doe
date: 2024-01-01
---
# Page Content HereCode Blocks
````markdown
def hello():
print("Hello, World!")````
Tables
| Header 1 | Header 2 |
| -------- | -------- |
| Cell 1 | Cell 2 |Theme Configuration
Built-in Themes
# Default MkDocs theme
theme:
name: mkdocs
color_mode: auto # light, dark, auto
user_color_mode_toggle: true
nav_style: primary # primary, dark, light
highlightjs: true
navigation_depth: 2
locale: en
# ReadTheDocs theme
theme:
name: readthedocs
prev_next_buttons_location: bottom
navigation_depth: 4
collapse_navigation: trueMaterial for MkDocs (Popular Third-Party)
pip install mkdocs-materialtheme:
name: material
palette:
primary: indigo
accent: indigo
features:
- navigation.tabs
- navigation.sections
- search.suggestCustom CSS/JavaScript
extra_css:
- css/extra.css
extra_javascript:
- js/extra.js
- path: js/analytics.mjs
type: modulePlugins
plugins:
- search:
lang: en
min_search_length: 3
- tags
- blogPopular Plugins:
search- Full-text search (built-in, enabled by default)blog- Blog functionality (Material theme)tags- Content categorizationsocial- Social media cards
Note: Definingpluginsdisables defaults. Add- searchexplicitly.
Markdown Extensions
markdown_extensions:
- toc:
permalink: true
separator: "-"
- tables
- fenced_code
- admonition
- pymdownx.highlight
- pymdownx.superfencesDeployment
GitHub Pages
# Deploy to gh-pages branch
mkdocs gh-deploy
# With options
mkdocs gh-deploy --force --message "Deploy docs"Build for Any Host
# Build static files
mkdocs build
# Files output to site/ directory
# Upload to any static hostCustom Domain
Create docs/CNAME file:
docs.example.comCommon Workflows
New Documentation Project
1. Create project: mkdocs new my-docs 2. Edit mkdocs.yml with site_name and nav 3. Add Markdown files to docs/ 4. Preview: mkdocs serve 5. Build: mkdocs build 6. Deploy: mkdocs gh-deploy
Quick Build Preview
Bash(mkdocs build --dry-run)
If clean: Bash(mkdocs serve -v) (dev preview).
Add New Section
1. Create directory: docs/new-section/ 2. Add index.md and content files 3. Update nav in mkdocs.yml 4. Preview and verify links
Customize Theme
1. Set theme.custom_dir: custom_theme/ 2. Create override files matching theme structure 3. Use template blocks to extend base templates
Safe Preview Workflow
1. Check MkDocs: Bash(which mkdocs || echo "Install: pip install mkdocs") 2. Dry-run build: Bash(mkdocs build --dry-run) 3. List issues: Grep -r "ERROR" site/
Detailed References
- Configuration options: See references/configuration.md
- Theme customization: See references/themes.md
- Plugin development: See references/plugins.md
- Deployment strategies: See references/deployment.md
- Best practices: See references/best-practices.md
---
Gotchas
- `mkdocs serve` watches `docs/` and `mkdocs.yml` but NOT files included via `include_dir` or theme overrides — edits to
custom_theme/main.htmldon't trigger reload. Restart the server. - Defining `plugins:` in mkdocs.yml disables the default search plugin — pages stop being indexed and the search box returns nothing. Always include
- searchexplicitly when listing plugins. - `mkdocs gh-deploy` force-pushes to `gh-pages` — any manual edits or other branches deployed there get destroyed silently. Use
--no-historyfor clean history but never editgh-pagesby hand. - `use_directory_urls: true` (default) changes link semantics:
page.mdbecomespage/notpage.html. Relative links in raw Markdown that worked locally as files break on the deployed site. - `strict: true` fails on warnings including unrecognized config keys — adding a Material-theme-only option to a config that uses the default theme fails the build, not just warns. Check theme compatibility before enabling strict.
- Material theme's `navigation.instant` feature breaks third-party JS that runs on page load — analytics, Mermaid, MathJax all need explicit
document$.subscribe()hooks instead ofDOMContentLoaded. - `mkdocs build --dirty` skips unchanged files but doesn't detect changes to navigation or theme config — pages render with stale nav. Use
--clean(default) or deletesite/when in doubt.
MkDocs API Documentation Guide
Complete guide to generating API documentation from code.
mkdocstrings
Auto-generate documentation from Python docstrings.
Installation
pip install mkdocstrings[python]Basic Configuration
# mkdocs.yml
plugins:
- mkdocstrings:
handlers:
python:
paths: [src]
options:
show_source: true
show_root_heading: trueUsage in Markdown
# API Reference
## MyClass
::: mypackage.mymodule.MyClass
options:
show_source: true
members:
- __init__
- process
- saveAuto-Generate All Modules
Use with gen-files plugin for automatic generation:
plugins:
- gen-files:
scripts:
- scripts/gen_ref_pages.py
- literate-nav:
nav_file: SUMMARY.md
- mkdocstringsscripts/gen_ref_pages.py:
"""Generate API reference pages."""
from pathlib import Path
import mkdocs_gen_files
nav = mkdocs_gen_files.Nav()
src = Path("src")
for path in sorted(src.rglob("*.py")):
module_path = path.relative_to(src).with_suffix("")
doc_path = path.relative_to(src).with_suffix(".md")
full_doc_path = Path("reference", doc_path)
parts = tuple(module_path.parts)
if parts[-1] == "__init__":
parts = parts[:-1]
doc_path = doc_path.with_name("index.md")
full_doc_path = full_doc_path.with_name("index.md")
elif parts[-1] == "__main__":
continue
nav[parts] = doc_path.as_posix()
with mkdocs_gen_files.open(full_doc_path, "w") as fd:
ident = ".".join(parts)
fd.write(f"::: {ident}")
mkdocs_gen_files.set_edit_path(full_doc_path, path)
with mkdocs_gen_files.open("reference/SUMMARY.md", "w") as nav_file:
nav_file.writelines(nav.build_literate_nav())Configuration Options
plugins:
- mkdocstrings:
default_handler: python
handlers:
python:
paths: [src]
options:
# Headings
show_root_heading: true
show_root_full_path: false
show_root_toc_entry: true
heading_level: 2
# Members
members: true
members_order: source # source, alphabetical
filters:
- "!^_" # exclude private
- "^__init__$" # include __init__
group_by_category: true
show_category_heading: true
# Docstrings
docstring_style: google # google, numpy, sphinx
docstring_options:
ignore_init_summary: true
show_if_no_docstring: false
# Signatures
show_signature: true
show_signature_annotations: true
separate_signature: true
line_length: 80
# Source
show_source: true
show_bases: true
show_submodules: true
# Inheritance
inherited_members: false
merge_init_into_class: trueDocstring Styles
Google Style (Recommended):
def fetch_data(url: str, timeout: int = 30) -> dict:
"""Fetch data from the specified URL.
Args:
url: The URL to fetch data from.
timeout: Request timeout in seconds.
Returns:
A dictionary containing the response data.
Raises:
HTTPError: If the request fails.
TimeoutError: If the request times out.
Examples:
>>> fetch_data("https://api.example.com/data")
{'status': 'ok', 'data': [...]}
"""NumPy Style:
def fetch_data(url: str, timeout: int = 30) -> dict:
"""
Fetch data from the specified URL.
Parameters
----------
url : str
The URL to fetch data from.
timeout : int, optional
Request timeout in seconds (default: 30).
Returns
-------
dict
A dictionary containing the response data.
Raises
------
HTTPError
If the request fails.
TimeoutError
If the request times out.
"""Sphinx Style:
def fetch_data(url: str, timeout: int = 30) -> dict:
"""Fetch data from the specified URL.
:param url: The URL to fetch data from.
:type url: str
:param timeout: Request timeout in seconds.
:type timeout: int
:returns: A dictionary containing the response data.
:rtype: dict
:raises HTTPError: If the request fails.
:raises TimeoutError: If the request times out.
"""Cross-References
Link to other objects:
See the [`MyClass`][mypackage.mymodule.MyClass] for more details.
The [`process`][mypackage.mymodule.MyClass.process] method handles data.Multi-Language Support
mkdocstrings supports multiple languages via handlers:
# Python (default)
pip install mkdocstrings[python]
# Crystal
pip install mkdocstrings[crystal]
# Shell/Bash
pip install mkdocstrings-shellmkdocs-click
Generate documentation for Click CLI applications.
Installation
pip install mkdocs-clickConfiguration
# mkdocs.yml
markdown_extensions:
- mkdocs-clickUsage
# CLI Reference
::: mkdocs_click
:module: myapp.cli
:command: main
:prog_name: myapp
:depth: 2
:style: table # table, plainClick Application Example
# myapp/cli.py
import click
@click.group()
@click.option('--verbose', '-v', is_flag=True, help='Enable verbose output.')
def main(verbose):
"""MyApp - A sample CLI application.
This application demonstrates Click documentation generation.
"""
pass
@main.command()
@click.argument('name')
@click.option('--greeting', '-g', default='Hello', help='Greeting to use.')
def greet(name, greeting):
"""Greet a person by name.
This command outputs a personalized greeting message.
"""
click.echo(f"{greeting}, {name}!")
@main.command()
@click.option('--count', '-n', default=1, help='Number of times to repeat.')
def repeat(count):
"""Repeat a message multiple times."""
for _ in range(count):
click.echo("Repeating...")mkdocs-swagger-ui-tag
Embed OpenAPI/Swagger documentation.
Installation
pip install mkdocs-swagger-ui-tagConfiguration
plugins:
- swagger-ui-tag:
supportedSubmitMethods: [] # Disable "Try it out"
syntaxHighlightTheme: monokaiUsage
# API Documentation
<swagger-ui src="./openapi.yaml"/>Or with remote spec:
<swagger-ui src="https://petstore.swagger.io/v2/swagger.json"/>Configuration Options
plugins:
- swagger-ui-tag:
background: White
docExpansion: list # none, list, full
filter: true
syntaxHighlightTheme: monokai
tryItOutEnabled: false
supportedSubmitMethods: []
validatorUrl: nonemkdocs-redoc
Alternative OpenAPI renderer using ReDoc.
Installation
pip install mkdocs-render-swagger-pluginConfiguration
plugins:
- render_swaggerUsage
# API Reference
!!swagger openapi.yaml!!mkdocs-typer
Generate documentation for Typer CLI applications.
Installation
pip install mkdocs-typerConfiguration
markdown_extensions:
- mkdocs-typerUsage
::: mkdocs-typer
:module: myapp.cli
:command: app
:prog_name: myappgriffe
Modern Python API documentation tool (used by mkdocstrings).
Direct Usage
# Generate API inventory
import griffe
package = griffe.load("mypackage")
for module in package.modules.values():
print(f"Module: {module.name}")
for cls in module.classes.values():
print(f" Class: {cls.name}")
for method in cls.functions.values():
print(f" Method: {method.name}")Extensions
Create custom griffe extensions:
# extensions.py
from griffe import Extension, Object, ObjectNode
class MyExtension(Extension):
def on_instance(self, node: ObjectNode, obj: Object) -> None:
# Modify object attributes
if obj.is_function:
obj.labels.add("custom-label")plugins:
- mkdocstrings:
handlers:
python:
options:
extensions:
- extensions:MyExtensionCombining Tools
Full API Documentation Setup
# mkdocs.yml
theme:
name: material
plugins:
- search
- gen-files:
scripts:
- scripts/gen_ref_pages.py
- literate-nav:
nav_file: SUMMARY.md
- section-index
- mkdocstrings:
handlers:
python:
paths: [src]
options:
show_source: true
show_root_heading: true
docstring_style: google
merge_init_into_class: true
group_by_category: true
- swagger-ui-tag
markdown_extensions:
- mkdocs-click
- pymdownx.highlight
- pymdownx.superfencesNavigation Structure
nav:
- Home: index.md
- User Guide:
- Getting Started: guide/getting-started.md
- Configuration: guide/configuration.md
- API Reference:
- Overview: reference/index.md
- Python API: reference/ # Auto-generated
- REST API:
- Endpoints: api/endpoints.md # Swagger UI
- CLI Reference:
- Commands: cli/commands.md # mkdocs-clickBest Practices
Write Good Docstrings
class DataProcessor:
"""Process and transform data from various sources.
This class provides methods for loading, transforming, and
exporting data in multiple formats.
Attributes:
source: The data source path or URL.
format: The output format (json, csv, parquet).
Example:
>>> processor = DataProcessor("data.csv")
>>> processor.transform()
>>> processor.export("output.json")
"""
def __init__(self, source: str, format: str = "json") -> None:
"""Initialize the data processor.
Args:
source: Path or URL to the data source.
format: Output format. Defaults to "json".
"""
self.source = source
self.format = formatOrganize API Reference
docs/
├── reference/
│ ├── index.md # API overview
│ ├── SUMMARY.md # Auto-generated nav
│ ├── core/
│ │ ├── index.md
│ │ └── processor.md
│ └── utils/
│ ├── index.md
│ └── helpers.mdUse Type Hints
from typing import Optional, List, Dict, Any
def process_items(
items: List[Dict[str, Any]],
filter_fn: Optional[callable] = None,
limit: int = 100
) -> List[Dict[str, Any]]:
"""Process a list of items with optional filtering.
Args:
items: List of item dictionaries to process.
filter_fn: Optional function to filter items.
limit: Maximum number of items to return.
Returns:
Processed and optionally filtered list of items.
"""Add Examples
def parse_config(path: str) -> dict:
"""Parse a configuration file.
Args:
path: Path to the configuration file.
Returns:
Parsed configuration as a dictionary.
Examples:
Basic usage:
>>> config = parse_config("config.yaml")
>>> config["database"]["host"]
'localhost'
With environment variables:
>>> import os
>>> os.environ["DB_HOST"] = "production.db"
>>> config = parse_config("config.yaml")
>>> config["database"]["host"]
'production.db'
"""Comparison Table
| Tool | Purpose | Input | Output |
|---|---|---|---|
| mkdocstrings | Python API docs | Docstrings | Markdown |
| mkdocs-click | Click CLI docs | Click app | Markdown |
| mkdocs-typer | Typer CLI docs | Typer app | Markdown |
| swagger-ui-tag | REST API docs | OpenAPI spec | Interactive UI |
| gen-files | Auto-generate | Python | Markdown files |
MkDocs Best Practices
Guidelines for creating high-quality documentation sites.
Project Organization
Directory Structure
project/
├── mkdocs.yml # Configuration
├── docs/
│ ├── index.md # Homepage
│ ├── getting-started/
│ │ ├── index.md # Section intro
│ │ ├── installation.md
│ │ └── quick-start.md
│ ├── user-guide/
│ │ ├── index.md
│ │ ├── configuration.md
│ │ └── advanced.md
│ ├── api/
│ │ └── reference.md
│ ├── assets/
│ │ ├── images/
│ │ └── files/
│ └── stylesheets/
│ └── extra.css
├── overrides/ # Theme customizations
│ └── main.html
└── scripts/ # Build scripts
└── generate-api-docs.pyNaming Conventions
- Use lowercase filenames:
getting-started.md - Use hyphens for spaces:
user-guide.mdnotuser_guide.md - Use
index.mdfor directory homepages - Consistent naming across sections
File Organization
- One topic per file: Each file covers one concept
- Logical grouping: Related topics in same directory
- Progressive depth: Overview → Details → Advanced
- Keep files focused: Under 500 lines ideally
Writing Quality
Document Structure
# Page Title
Brief introduction explaining what this page covers.
## First Major Section
Content here...
### Subsection
More detailed content...
## Second Major Section
More content...
## See Also
- [Related Page](related.md)
- [External Resource](https://example.com)Heading Hierarchy
- One
#heading per page (document title) - Use sequential levels:
##→###→#### - Don't skip levels
- Keep headings concise
Code Examples
Always include language identifier: ````markdown
def hello():
print("Hello, World!")````
Show both input and output: ````markdown
$ mkdocs serve
INFO - Building documentation...
INFO - Serving on http://127.0.0.1:8000````
Use annotations (Material theme): ````markdown
def process(data): # (1)!
return data.strip() # (2)!1. Function accepts any string data 2. Removes leading/trailing whitespace ````
Links
Internal links - use relative paths:
[Configuration Guide](configuration.md)
[Installation](../getting-started/installation.md)
[Options Section](configuration.md#options)External links - open in new tab (Material):
[Python Docs](https://docs.python.org/){target=_blank}Avoid:
- Absolute paths:
/docs/guide.md - URLs for internal links
- Broken anchors
Configuration
Essential Settings
# Always set these
site_name: Project Name
site_url: https://docs.example.com/
# Highly recommended
site_description: Brief project description
repo_url: https://github.com/org/project
# SEO and linking
edit_uri: edit/main/docs/
copyright: Copyright 2024 OrganizationNavigation Best Practices
nav:
- Home: index.md
# Group related items
- Getting Started:
- Installation: getting-started/installation.md
- Quick Start: getting-started/quick-start.md
# Limit nesting to 2-3 levels
- User Guide:
- Overview: user-guide/index.md
- Configuration: user-guide/configuration.md
# Keep most important items first
- API Reference: api/
# External links at end
- GitHub: https://github.com/org/projectValidation
# Enable strict validation
strict: true
validation:
nav:
omitted_files: warn
not_found: warn
absolute_links: warn
links:
not_found: warn
anchors: warnPerformance
Build Performance
# For large sites, pre-build search index
plugins:
- search:
prebuild_index: true
# During development, use dirty builds
# mkdocs serve --dirtyPage Load Performance
# Minify output
plugins:
- minify:
minify_html: true
minify_js: true
# Optimize images before adding to docs
# Use WebP format when possibleImage Optimization
# Optimize PNGs
optipng -o7 docs/images/*.png
# Convert to WebP
cwebp -q 80 image.png -o image.webpInclude proper alt text:
SEO
Page Metadata
---
title: Configuration Guide
description: Complete guide to configuring Project Name
---
# Configuration GuideSite Configuration
site_name: Project Name
site_url: https://docs.example.com/
site_description: Documentation for Project Name
# Social metadata (Material theme)
extra:
social:
- icon: fontawesome/brands/github
link: https://github.com/org/project
- icon: fontawesome/brands/twitter
link: https://twitter.com/projectSitemap
MkDocs generates sitemap.xml automatically when site_url is set.
Accessibility
Images
Links
<!-- Good: Descriptive text -->
[Read the installation guide](installation.md)
<!-- Avoid: Vague text -->
[Click here](installation.md)Headings
- Use proper heading hierarchy
- Don't use headings just for styling
- Keep headings descriptive
Colors
- Ensure sufficient contrast
- Don't rely on color alone
- Test with accessibility tools
Version Control
.gitignore
# MkDocs build output
site/
# Python
__pycache__/
*.py[cod]
.venv/
venv/
# IDE
.idea/
.vscode/
# OS
.DS_Store
Thumbs.dbCI/CD Checks
# .github/workflows/docs.yml
name: Documentation
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- run: pip install mkdocs mkdocs-material
- run: mkdocs build --strictContent Maintenance
Regular Reviews
- Check for broken links quarterly
- Update outdated content
- Remove deprecated sections
- Verify code examples work
Changelog
Maintain a changelog for documentation:
# Changelog
## 2024-01-15
- Added API authentication section
- Updated installation guide for v2.0
- Fixed broken links in user guideDeprecation
!!! warning "Deprecated"
This feature is deprecated and will be removed in v3.0.
See [New Feature](new-feature.md) for the replacement.Common Patterns
Tabbed Content (Material)
=== "Python"
print("Hello")
=== "JavaScript"
console.log("Hello");
Admonitions
!!! note
This is a note.
!!! warning
This is a warning.
!!! danger
This is dangerous!
!!! tip
This is a helpful tip.Collapsible Sections
??? note "Click to expand"
Hidden content here.
???+ note "Expanded by default"
Visible content here.Checklist
Before Publishing
- [ ] All pages have descriptive titles
- [ ] Navigation is logical and complete
- [ ] Internal links work (
mkdocs build --strict) - [ ] Code examples are tested
- [ ] Images have alt text
- [ ] site_url is configured correctly
- [ ] Search works properly
- [ ] Mobile view is acceptable
- [ ] 404 page exists
Regular Maintenance
- [ ] Check for broken external links
- [ ] Update deprecated content
- [ ] Review and update code examples
- [ ] Check analytics for problem pages
- [ ] Test search functionality
- [ ] Verify deployment pipeline works
MkDocs Configuration Reference
Complete reference for all mkdocs.yml configuration options.
Site Information
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
site_name | String | Yes | None | Main title for your project |
site_url | String | No | null | Canonical URL (adds link tag in head) |
site_description | String | No | null | Site description meta tag |
site_author | String | No | null | Author name meta tag |
copyright | String | No | null | Copyright text in footer |
Repository Settings
| Option | Type | Default | Description |
|---|---|---|---|
repo_url | String | null | Repository link (GitHub/GitLab/Bitbucket) |
repo_name | String | Auto-detected | Repository link text |
edit_uri | String | edit/master/docs/ | Path for "Edit on GitHub" links |
edit_uri_template | String | null | Template with {path} placeholder |
remote_branch | String | gh-pages | GitHub Pages deploy branch |
remote_name | String | origin | Git remote name for deploy |
Build Directories
| Option | Type | Default | Description |
|---|---|---|---|
docs_dir | String | docs | Source markdown directory |
site_dir | String | site | Output HTML directory |
Navigation
# Automatic (alphabetically sorted if omitted)
# nav: auto-generated
# Explicit navigation
nav:
- Home: index.md
- 'User Guide':
- Overview: user-guide/overview.md
- Installation: user-guide/install.md
- API Reference: api/index.md
- GitHub: https://github.com/example/repo
# File patterns
exclude_docs: |
/drafts/
*.py
/templates/
draft_docs: |
drafts/
not_in_nav: |
snippets/Theme Configuration
theme:
name: mkdocs # or 'readthedocs', 'material', etc.
locale: en # Language code
custom_dir: custom_theme/ # Override directory
# mkdocs theme options
color_mode: auto # light, dark, auto
user_color_mode_toggle: true
nav_style: primary # primary, dark, light
highlightjs: true
hljs_style: github
hljs_style_dark: github-dark
hljs_languages:
- yaml
- rust
navigation_depth: 2
shortcuts:
help: 191 # ? key
next: 78 # n key
previous: 80 # p key
search: 83 # s key
analytics:
gtag: G-XXXXXXXXXX
# readthedocs theme options
prev_next_buttons_location: bottom # bottom, top, both, none
navigation_depth: 4
collapse_navigation: true
titles_only: false
sticky_navigation: true
include_homepage_in_sidebar: true
logo: img/logo.pngAssets
extra_css:
- css/extra.css
- css/print.css
extra_javascript:
- js/extra.js
- path: js/analytics.mjs
type: module
- path: js/deferred.js
defer: true
- path: js/async.js
async: true
extra_templates:
- sitemap.htmlMarkdown Extensions
markdown_extensions:
# Built-in (always active)
- meta # Page metadata
- toc: # Table of contents
permalink: true
permalink_title: Link to this section
baselevel: 1
separator: "-"
toc_depth: 3
- tables # Table syntax
- fenced_code # Code blocks
# Common additions
- smarty # Smart quotes
- admonition # Note/warning boxes
- abbr # Abbreviations
- attr_list # Attribute lists
- def_list # Definition lists
- footnotes # Footnotes
- md_in_html # Markdown inside HTML
# PyMdown Extensions (pip install pymdown-extensions)
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
- pymdownx.details
- pymdownx.emoji
- pymdownx.keys
- pymdownx.mark
- pymdownx.critic
- pymdownx.caret
- pymdownx.tildePlugins
plugins:
- search:
separator: '[\s\-]+'
min_search_length: 3
lang:
- en
- fr
prebuild_index: false # true for large sites
indexing: full # full, sections, titles
- tags:
tags_file: tags.md
- blog:
enabled: true
post_date_format: short
# Conditional activation
- code-validator:
enabled: !ENV [CI, false]Important: Defining plugins disables defaults. Re-add search explicitly.
Build Settings
| Option | Type | Default | Description |
|---|---|---|---|
use_directory_urls | Boolean | true | Pretty URLs (/about/ vs /about.html) |
strict | Boolean | false | Fail build on warnings |
dev_addr | String | 127.0.0.1:8000 | Development server address |
watch | List | [] | Additional directories to watch |
Validation
validation:
nav:
omitted_files: warn # warn, info, ignore
not_found: warn
absolute_links: warn
links:
not_found: warn
anchors: warn
absolute_links: relative_to_docs # MkDocs 1.6+
unrecognized_links: warnHooks
hooks:
- my_hooks.pyHook file (my_hooks.py):
def on_page_markdown(markdown, page, config, files):
return markdown.replace('OLD', 'NEW')
def on_post_build(config):
print("Build complete!")Custom Variables
extra:
version: 1.0.0
environment: production
social:
- icon: fontawesome/brands/github
link: https://github.com/example
- icon: fontawesome/brands/twitter
link: https://twitter.com/exampleAccess in templates: {{ config.extra.version }}
Environment Variables
# Single variable
site_name: !ENV SITE_NAME
# With fallback
site_name: !ENV [SITE_NAME, 'Default Name']
# Multiple fallbacks
api_key: !ENV [API_KEY_PROD, API_KEY_DEV, 'default']Configuration Inheritance
# child.yml
INHERIT: ../base.yml
site_name: Child Project
# Overrides base.yml settingsNotes:
- Uses deep merge for dictionaries
- Lists are replaced, not appended
- Navigation cannot be merged
Complete Example
site_name: My Project
site_url: https://docs.example.com/
site_description: Project documentation
site_author: John Doe
copyright: Copyright 2024 Example Inc.
repo_url: https://github.com/example/project
repo_name: GitHub
edit_uri: edit/main/docs/
docs_dir: docs
site_dir: build
nav:
- Home: index.md
- Getting Started:
- Installation: getting-started/installation.md
- Quick Start: getting-started/quickstart.md
- User Guide:
- Configuration: user-guide/configuration.md
- Advanced: user-guide/advanced.md
- API Reference: api/
- Changelog: changelog.md
- GitHub: https://github.com/example/project
theme:
name: material
palette:
primary: indigo
features:
- navigation.tabs
- search.suggest
custom_dir: overrides/
extra_css:
- stylesheets/extra.css
extra_javascript:
- scripts/extra.js
markdown_extensions:
- toc:
permalink: true
- admonition
- pymdownx.highlight
- pymdownx.superfences
- pymdownx.tabbed:
alternate_style: true
plugins:
- search:
lang: en
- tags
validation:
nav:
omitted_files: warn
links:
not_found: warn
extra:
version: !ENV [VERSION, 'dev']
analytics:
provider: google
property: G-XXXXXXXXXXMkDocs Deployment Guide
Complete guide to deploying MkDocs documentation sites.
Build for Deployment
# Build static site
mkdocs build
# Output in site/ directory by default
mkdocs build --site-dir ./output
# Clean build (remove old files)
mkdocs build --clean
# Strict mode (fail on warnings)
mkdocs build --strictGitHub Pages
Quick Deploy
# Deploy to gh-pages branch
mkdocs gh-deployThis command: 1. Builds documentation 2. Creates/updates gh-pages branch 3. Pushes to GitHub 4. GitHub serves from gh-pages branch
Deploy Options
# Custom commit message
mkdocs gh-deploy --message "Deploy version 1.2.0"
# Different branch
mkdocs gh-deploy --remote-branch docs
# Different remote
mkdocs gh-deploy --remote-name upstream
# Force push (overwrites history)
mkdocs gh-deploy --force
# Single commit (no history)
mkdocs gh-deploy --no-history
# Skip version check
mkdocs gh-deploy --ignore-version
# Use git shell commands
mkdocs gh-deploy --shellProject Pages vs User Pages
Project Pages (default):
- URL:
username.github.io/project-name/ - Uses:
gh-pagesbranch
User/Organization Pages:
# From project repo, deploy to user repo
cd ../username.github.io/
mkdocs gh-deploy \
--config-file ../my-project/mkdocs.yml \
--remote-branch masterGitHub Actions CI/CD
.github/workflows/docs.yml:
name: Deploy Documentation
on:
push:
branches:
- main
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
pip install mkdocs
pip install mkdocs-material
pip install $(mkdocs get-deps)
- name: Build and deploy
run: mkdocs gh-deploy --forceWith Material Theme Insiders:
name: Deploy Documentation
on:
push:
branches:
- main
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- run: pip install mkdocs-material
- name: Configure Git
run: |
git config user.name github-actions
git config user.email github-actions@github.com
- run: mkdocs gh-deploy --forceCustom Domain
1. Create CNAME file:
Create docs/CNAME with your domain:
docs.example.com2. Configure DNS:
- CNAME record:
docs.example.com→username.github.io - Or A records for apex domain
3. Enable HTTPS:
- Go to repository Settings → Pages
- Enable "Enforce HTTPS"
mkdocs.yml:
site_url: https://docs.example.com/Read the Docs
Setup
1. Connect repository to readthedocs.org 2. Import project 3. Configure build settings
Configuration
.readthedocs.yaml:
version: 2
build:
os: ubuntu-22.04
tools:
python: "3.11"
mkdocs:
configuration: mkdocs.yml
python:
install:
- requirements: docs/requirements.txtdocs/requirements.txt:
mkdocs>=1.5
mkdocs-material>=9.0
pymdown-extensions>=10.0Features
- Automatic builds on push
- Version management
- PDF/EPUB generation
- Search integration
- Custom domains
- Pull request previews
GitLab Pages
.gitlab-ci.yml:
image: python:3.11-alpine
pages:
stage: deploy
script:
- pip install mkdocs mkdocs-material
- mkdocs build --site-dir public
artifacts:
paths:
- public
only:
- mainNetlify
Automatic Deploy
1. Connect repository to Netlify 2. Configure build settings:
- Build command:
mkdocs build - Publish directory:
site
netlify.toml:
[build]
command = "mkdocs build"
publish = "site"
[build.environment]
PYTHON_VERSION = "3.11"
[[redirects]]
from = "/*"
to = "/index.html"
status = 200Deploy Previews
Netlify automatically creates preview deployments for pull requests.
Vercel
vercel.json:
{
"buildCommand": "pip install mkdocs mkdocs-material && mkdocs build",
"outputDirectory": "site",
"installCommand": "pip install mkdocs"
}Cloudflare Pages
1. Connect repository 2. Build settings:
- Framework preset: None
- Build command:
pip install mkdocs && mkdocs build - Build output directory:
site
AWS S3 + CloudFront
Build and Upload
# Build
mkdocs build
# Sync to S3
aws s3 sync site/ s3://your-bucket-name/ \
--delete \
--cache-control "max-age=86400"
# Invalidate CloudFront
aws cloudfront create-invalidation \
--distribution-id YOUR_DISTRIBUTION_ID \
--paths "/*"Terraform Configuration
resource "aws_s3_bucket" "docs" {
bucket = "docs.example.com"
}
resource "aws_s3_bucket_website_configuration" "docs" {
bucket = aws_s3_bucket.docs.id
index_document {
suffix = "index.html"
}
error_document {
key = "404.html"
}
}
resource "aws_cloudfront_distribution" "docs" {
origin {
domain_name = aws_s3_bucket.docs.bucket_regional_domain_name
origin_id = "S3-docs"
}
enabled = true
default_root_object = "index.html"
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "S3-docs"
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
viewer_protocol_policy = "redirect-to-https"
min_ttl = 0
default_ttl = 86400
max_ttl = 31536000
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
viewer_certificate {
cloudfront_default_certificate = true
}
}Self-Hosted / Generic Hosting
Build and Transfer
# Build static files
mkdocs build
# Transfer via SCP
scp -r site/* user@server:/var/www/html/
# Or rsync
rsync -avz --delete site/ user@server:/var/www/html/
# Or FTP
lftp -u user,password server -e "mirror -R site/ /public_html; quit"Nginx Configuration
server {
listen 80;
server_name docs.example.com;
root /var/www/docs;
index index.html;
location / {
try_files $uri $uri/ $uri.html =404;
}
error_page 404 /404.html;
# Cache static assets
location ~* \.(css|js|png|jpg|gif|ico|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}Apache Configuration
<VirtualHost *:80>
ServerName docs.example.com
DocumentRoot /var/www/docs
<Directory /var/www/docs>
Options -Indexes +FollowSymLinks
AllowOverride All
Require all granted
</Directory>
ErrorDocument 404 /404.html
# Cache static assets
<FilesMatch "\.(css|js|png|jpg|gif|ico|woff2)$">
ExpiresActive On
ExpiresDefault "access plus 1 year"
</FilesMatch>
</VirtualHost>Offline / Local Distribution
Configuration for Offline Use
site_url: ""
use_directory_urls: false
plugins: [] # Disable search (requires JavaScript)Create Distribution Package
# Build with offline settings
mkdocs build
# Create archive
cd site
zip -r ../documentation.zip .
# or
tar -czvf ../documentation.tar.gz .PDF Generation
pip install mkdocs-pdf-export-pluginplugins:
- search
- pdf-export:
enabled_if_env: ENABLE_PDF
combined: trueVersion Management
Multiple Versions with mike
pip install mike# Deploy version
mike deploy 1.0 latest -u
# Deploy specific version
mike deploy 1.1
# Set default
mike set-default latest
# List versions
mike listmkdocs.yml:
extra:
version:
provider: mikeTroubleshooting
Common Issues
404 errors on refresh:
- Ensure
use_directory_urls: true - Configure server for SPA routing
Broken internal links:
- Use relative paths
- Link to
.mdfiles, not.html - Run
mkdocs build --strictto catch issues
Search not working:
- Ensure search plugin is enabled
- Check for JavaScript errors
- Verify
search_index.jsonexists
Large site slow to build:
- Enable
prebuild_index: truein search config - Use
--dirtyflag for incremental builds
Verification
# Serve locally and verify
mkdocs serve
# Check for broken links
mkdocs build --strict
# Verify all pages accessible
curl -s http://localhost:8000/sitemap.xml | grep -o '<loc>[^<]*</loc>'MkDocs Diagrams Guide
Complete guide to adding diagrams and visualizations to MkDocs documentation.
Native Mermaid (Material Theme)
Material for MkDocs has built-in Mermaid support - no additional plugins needed.
Configuration
# mkdocs.yml
theme:
name: material
markdown_extensions:
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_formatFlowcharts
````markdown
graph LR
A[Start] --> B{Decision}
B -->|Yes| C[Action 1]
B -->|No| D[Action 2]
C --> E[End]
D --> E````
Direction Options:
TBorTD- Top to BottomBT- Bottom to TopLR- Left to RightRL- Right to Left
Sequence Diagrams
````markdown
sequenceDiagram
participant U as User
participant A as API
participant D as Database
U->>A: POST /login
A->>D: Query user
D-->>A: User data
A-->>U: JWT token````
Class Diagrams
````markdown
classDiagram
class Animal {
+String name
+int age
+makeSound()
}
class Dog {
+String breed
+bark()
}
Animal <|-- Dog````
State Diagrams
````markdown
stateDiagram-v2
[*] --> Idle
Idle --> Processing : Start
Processing --> Complete : Success
Processing --> Error : Failure
Complete --> [*]
Error --> Idle : Retry````
Entity Relationship Diagrams
````markdown
erDiagram
CUSTOMER ||--o{ ORDER : places
ORDER ||--|{ LINE_ITEM : contains
PRODUCT ||--o{ LINE_ITEM : "ordered in"
CUSTOMER {
string name
string email
}
ORDER {
int id
date created
}````
Gantt Charts
````markdown
gantt
title Project Schedule
dateFormat YYYY-MM-DD
section Planning
Requirements :a1, 2024-01-01, 30d
Design :a2, after a1, 20d
section Development
Implementation :b1, after a2, 45d
Testing :b2, after b1, 15d````
Pie Charts
````markdown
pie title Distribution
"Category A" : 45
"Category B" : 30
"Category C" : 25````
Git Graphs
````markdown
gitGraph
commit id: "Initial"
branch feature
commit id: "Add feature"
checkout main
commit id: "Fix bug"
merge feature
commit id: "Release"````
User Journey
````markdown
journey
title User Onboarding
section Sign Up
Visit website: 5: User
Fill form: 3: User
Verify email: 4: User
section First Use
Complete profile: 4: User
Explore features: 5: User````
mkdocs-mermaid2-plugin
Standalone Mermaid plugin for themes without native support.
Installation
pip install mkdocs-mermaid2-pluginConfiguration
plugins:
- mermaid2:
version: 10.6.0
arguments:
theme: 'dark'
securityLevel: 'loose'
extra_javascript:
- https://unpkg.com/mermaid/dist/mermaid.min.jsKroki (PlantUML, C4, 20+ Diagram Types)
Kroki supports 20+ diagram types via a unified API.
Installation
pip install mkdocs-kroki-pluginConfiguration
plugins:
- kroki:
ServerURL: https://kroki.io
EnableBlockDiag: true
EnableExcalidraw: true
DownloadImages: false
DownloadDir: docs/images/krokiPlantUML Diagrams
````markdown
@startuml
actor User
participant "Web App" as App
database "Database" as DB
User -> App: Request
App -> DB: Query
DB --> App: Results
App --> User: Response
@enduml````
C4 Architecture Diagrams
````markdown
@startuml
!include https://raw.githubusercontent.com/plantuml-stdlib/C4-PlantUML/master/C4_Container.puml
Person(user, "User", "A user of the system")
System_Boundary(system, "System") {
Container(web, "Web App", "React", "Frontend")
Container(api, "API", "Python", "Backend")
ContainerDb(db, "Database", "PostgreSQL", "Data storage")
}
Rel(user, web, "Uses")
Rel(web, api, "Calls")
Rel(api, db, "Reads/Writes")
@enduml````
BlockDiag
````markdown
blockdiag {
A -> B -> C -> D;
A -> E -> F -> D;
}````
Network Diagrams
````markdown
nwdiag {
network dmz {
address = "210.x.x.x/24"
web01 [address = "210.x.x.1"];
web02 [address = "210.x.x.2"];
}
network internal {
address = "172.x.x.x/24";
web01 [address = "172.x.x.1"];
db01;
}
}````
Supported Diagram Types
| Type | Code Block | Description |
|---|---|---|
| PlantUML | plantuml | UML, C4, Archimate |
| Mermaid | mermaid | Various diagrams |
| BlockDiag | blockdiag | Block diagrams |
| SeqDiag | seqdiag | Sequence diagrams |
| ActDiag | actdiag | Activity diagrams |
| NwDiag | nwdiag | Network diagrams |
| PacketDiag | packetdiag | Packet structure |
| RackDiag | rackdiag | Server racks |
| ERD | erd | Entity-Relationship |
| GraphViz | graphviz | Graph visualization |
| Ditaa | ditaa | ASCII art diagrams |
| Nomnoml | nomnoml | UML-like diagrams |
| Vega | vega | Data visualization |
| Vega-Lite | vegalite | Simple charts |
| WaveDrom | wavedrom | Digital timing |
| Bytefield | bytefield | Byte/bit fields |
| Excalidraw | excalidraw | Hand-drawn style |
| BPMN | bpmn | Business process |
| Structurizr | structurizr | C4 model DSL |
D2 Diagrams
D2 is a modern declarative diagramming language.
Installation
pip install mkdocs-d2-pluginRequires D2 CLI:
# macOS
brew install d2
# Linux
curl -fsSL https://d2lang.com/install.sh | shConfiguration
plugins:
- d2:
executable: d2
theme: 0
layout: dagre
sketch: false
pad: 100Basic D2 Diagram
````markdown
direction: right
client -> server: request
server -> database: query
database -> server: results
server -> client: response````
D2 with Styling
````markdown
direction: down
User: {
shape: person
}
Web App: {
style.fill: "#e8f4ea"
}
Database: {
shape: cylinder
style.fill: "#ffeeba"
}
User -> Web App: HTTP
Web App -> Database: SQL````
D2 Containers
````markdown
AWS: {
VPC: {
Public Subnet: {
ALB
}
Private Subnet: {
ECS
RDS: {shape: cylinder}
}
ALB -> ECS
ECS -> RDS
}
}````
Pan & Zoom for Diagrams
Enable pan and zoom on diagrams and images.
Installation
pip install mkdocs-panzoomConfiguration
plugins:
- panzoom:
selector: "img, svg, .mermaid"
full_screen: true
zoom_button: trueMaterial Theme Diagram Recommendations
For Material for MkDocs, use this recommended setup:
theme:
name: material
features:
- content.code.copy
markdown_extensions:
- pymdownx.superfences:
custom_fences:
- name: mermaid
class: mermaid
format: !!python/name:pymdownx.superfences.fence_code_format
# For additional diagram types
plugins:
- kroki:
ServerURL: https://kroki.ioSelf-Hosted Kroki
For enterprise/offline use, self-host Kroki:
# docker-compose.yml
services:
kroki:
image: yuzutech/kroki
ports:
- "8000:8000"
environment:
- KROKI_MERMAID_HOST=mermaid
- KROKI_BPMN_HOST=bpmn
mermaid:
image: yuzutech/kroki-mermaid
bpmn:
image: yuzutech/kroki-bpmn# mkdocs.yml
plugins:
- kroki:
ServerURL: http://localhost:8000
DownloadImages: true
DownloadDir: docs/images/diagramsBest Practices
Keep Diagrams Simple
graph LR
A --> B --> C- Limit nodes to 10-15 per diagram
- Use clear, short labels
- Break complex systems into multiple diagrams
Use Meaningful Colors
````markdown
graph TD
subgraph "Frontend"
style Frontend fill:#e1f5fe
A[React App]
end
subgraph "Backend"
style Backend fill:#f3e5f5
B[API Server]
end
A --> B````
Add Context with Titles
````markdown
---
title: User Authentication Flow
---
sequenceDiagram
User->>App: Login
App->>Auth: Validate
Auth-->>App: Token
App-->>User: Success````
Version Control
- Store diagram source in Markdown (not images)
- Diagrams as code enables diffs and history
- Self-hosted Kroki caches images for reproducibility
Comparison Table
| Feature | Native Mermaid | Mermaid2 Plugin | Kroki | D2 |
|---|---|---|---|---|
| Setup | Easy | Medium | Medium | Medium |
| Diagram Types | 10+ | 10+ | 20+ | 10+ |
| PlantUML | No | No | Yes | No |
| C4 Diagrams | Limited | Limited | Yes | Yes |
| Offline | Yes | Yes | Needs server | Yes |
| Performance | Fast | Fast | Network | Fast |
| Material Theme | Built-in | Works | Works | Works |
MkDocs Plugins Guide
Complete guide to installing, configuring, and developing plugins.
Installing Plugins
# Install from PyPI
pip install mkdocs-plugin-name
# Common naming convention
pip install mkdocs-[name]-pluginConfiguring Plugins
# Basic configuration
plugins:
- search
- tags
- blog
# With options
plugins:
- search:
lang: en
min_search_length: 3
- tags:
tags_file: tags.md
# Dictionary syntax (for inheritance)
plugins:
search:
lang: en
tags:
tags_file: tags.md
# Conditional activation
plugins:
- search
- code-validator:
enabled: !ENV [CI, false]
# Disable all plugins
plugins: []Important: Defining plugins disables defaults (like search). Re-add explicitly.
Built-in Plugins
Search Plugin
Full-text search using lunr.js. Enabled by default.
plugins:
- search:
separator: '[\s\-]+' # Word delimiters regex
min_search_length: 3 # Minimum query length
lang: # Language support
- en
- fr
prebuild_index: false # Pre-build for large sites
indexing: full # full, sections, titlesIndexing Options:
full- Index all contentsections- Index by sectiontitles- Index titles only
Popular Third-Party Plugins
Material Theme Plugins
pip install mkdocs-materialBlog Plugin:
plugins:
- blog:
enabled: true
blog_dir: blog
post_date_format: short
post_url_format: "{date}/{slug}"
archive: true
categories: true
pagination: true
pagination_per_page: 10Tags Plugin:
plugins:
- tags:
tags_file: tags.md
tags_slugify: !!python/object/apply:pymdownx.slugs.slugify
kwds:
case: lowerSocial Plugin:
plugins:
- social:
enabled: !ENV [CI, false]
cards: true
cards_color:
fill: "#0FF1CE"
text: "#FFFFFF"
cards_font: RobotoSearch Plugin (Enhanced):
plugins:
- search:
separator: '[\s\-\.]+'
lang:
- enOffline Plugin:
plugins:
- offline:
enabled: !ENV [OFFLINE, false]Privacy Plugin:
plugins:
- privacy:
enabled: !ENV [CI, false]
assets_fetch: true
assets_fetch_dir: assets/externalOther Popular Plugins
Minify:
pip install mkdocs-minify-pluginplugins:
- minify:
minify_html: true
minify_js: true
minify_css: trueRedirects:
pip install mkdocs-redirectsplugins:
- redirects:
redirect_maps:
'old-page.md': 'new-page.md'
'old/path.md': 'new/path.md'Macros:
pip install mkdocs-macros-pluginplugins:
- macros:
module_name: main
include_dir: snippetsGit Revision Date:
pip install mkdocs-git-revision-date-localized-pluginplugins:
- git-revision-date-localized:
type: date
fallback_to_build_date: truePrint Site:
pip install mkdocs-print-site-pluginplugins:
- print-site:
add_to_navigation: true
print_page_title: 'Print Site'Plugin Development
Basic Plugin Structure
from mkdocs.plugins import BasePlugin
from mkdocs.config import config_options
class MyPlugin(BasePlugin):
config_scheme = (
('option_name', config_options.Type(str, default='default')),
('enabled', config_options.Type(bool, default=True)),
('count', config_options.Type(int, default=10)),
)
def on_config(self, config, **kwargs):
if not self.config['enabled']:
return config
# Modify config
return config
def on_page_markdown(self, markdown, page, config, files):
# Process markdown
return markdown.replace('OLD', 'NEW')Modern Config Pattern (MkDocs 1.4+)
from mkdocs.plugins import BasePlugin
from mkdocs.config.base import Config
from mkdocs.config.config_options import Type, Optional, ListOfItems
class MyPluginConfig(Config):
option_name = Type(str, default='default')
enabled = Type(bool, default=True)
items = ListOfItems(Type(str), default=[])
class MyPlugin(BasePlugin[MyPluginConfig]):
def on_pre_build(self, config, **kwargs):
# Access config as attributes
if self.config.enabled:
print(f"Option: {self.config.option_name}")Available Config Options
| Option Type | Description |
|---|---|
Type(type, default=value) | Basic typed option |
Optional(option) | Optional value |
File() | File path validation |
Dir() | Directory path validation |
Boolean() | Boolean values |
Integer() | Integer values |
Choice(choices) | Restricted choices |
URL() | URL format validation |
SubConfig(config_class) | Nested configuration |
ListOfItems(option) | List of validated items |
Plugin Events
One-Time Events:
| Event | Description |
|---|---|
on_startup(command, dirty) | Invocation start |
on_shutdown() | Invocation end |
on_serve(server, config, builder) | Server start |
Global Events:
| Event | Description |
|---|---|
on_config(config) | After config loaded |
on_pre_build(config) | Before build |
on_files(files, config) | After files collected |
on_nav(nav, config, files) | After nav created |
on_env(env, config, files) | After Jinja env created |
on_post_build(config) | After build complete |
on_build_error(error) | On any error |
Page Events:
| Event | Description |
|---|---|
on_pre_page(page, config, files) | Before page actions |
on_page_markdown(markdown, page, config, files) | After markdown loaded |
on_page_content(html, page, config, files) | After HTML rendered |
on_page_context(context, page, config, nav) | After context created |
on_post_page(output, page, config) | After page rendered |
Template Events:
| Event | Description |
|---|---|
on_pre_template(template, name, config) | After template loaded |
on_template_context(context, name, config) | After context created |
on_post_template(output, name, config) | After template rendered |
Event Priority
from mkdocs.plugins import event_priority, BasePlugin
class MyPlugin(BasePlugin):
@event_priority(100) # Run first
def on_files(self, files, config, **kwargs):
pass
@event_priority(-100) # Run last
def on_post_build(self, config, **kwargs):
passPriority Values:
100+- Run first50- Run early0- Default-50- Run late-100- Run last
Combined Events (MkDocs 1.6+)
from mkdocs.plugins import event_priority, CombinedEvent, BasePlugin
class MyPlugin(BasePlugin):
@event_priority(100)
def _on_page_markdown_first(self, markdown, **kwargs):
return markdown.upper()
@event_priority(-50)
def _on_page_markdown_last(self, markdown, **kwargs):
return markdown + "\n\n---\nGenerated content"
on_page_markdown = CombinedEvent(
_on_page_markdown_first,
_on_page_markdown_last
)Error Handling
from mkdocs.exceptions import PluginError
from mkdocs.plugins import BasePlugin
class MyPlugin(BasePlugin):
def on_page_markdown(self, markdown, page, **kwargs):
try:
result = self.process(markdown)
except KeyError as e:
raise PluginError(f"Missing required key: {e}")
return result
def on_build_error(self, error, **kwargs):
# Cleanup on error
self.cleanup()Logging
import logging
from mkdocs.plugins import get_plugin_logger
# Option 1: Direct logging
log = logging.getLogger(f"mkdocs.plugins.{__name__}")
# Option 2: Convenience function (MkDocs 1.5+)
log = get_plugin_logger(__name__)
class MyPlugin(BasePlugin):
def on_pre_build(self, config, **kwargs):
log.warning("Always shown") # Also fails in strict mode
log.info("With --verbose")
log.debug("With --debug")Packaging for Distribution
Project Structure:
mkdocs-myplugin/
├── setup.py
├── README.md
├── mkdocs_myplugin/
│ ├── __init__.py
│ └── plugin.pysetup.py:
from setuptools import setup, find_packages
setup(
name='mkdocs-myplugin',
version='1.0.0',
packages=find_packages(),
install_requires=['mkdocs>=1.0'],
entry_points={
'mkdocs.plugins': [
'myplugin = mkdocs_myplugin.plugin:MyPlugin',
]
},
python_requires='>=3.8',
)Native Hooks (MkDocs 1.4+)
Simple hooks without creating a package.
hooks:
- my_hooks.pymy_hooks.py:
def on_page_markdown(markdown, page, config, files):
"""Process markdown before rendering."""
return markdown.replace('{{VERSION}}', '1.0.0')
def on_post_build(config):
"""Run after build completes."""
print("Build complete!")
def on_files(files, config):
"""Modify files collection."""
for file in files:
if file.src_uri.endswith('.draft.md'):
files.remove(file)
return files
def on_page_context(context, page, config, nav):
"""Add variables to page context."""
context['custom_var'] = 'Custom Value'
return contextPlugin Discovery
Find plugins in the MkDocs Catalog.
Show Required Dependencies:
mkdocs get-deps
pip install $(mkdocs get-deps)MkDocs Themes Guide
Complete guide to theme selection, configuration, and customization.
Built-in Themes
mkdocs Theme (Default)
Bootstrap-based theme with modern features.
theme:
name: mkdocs
locale: en
color_mode: auto # light, dark, auto
user_color_mode_toggle: true # Show toggle button
nav_style: primary # primary, dark, light
highlightjs: true
hljs_style: github
hljs_style_dark: github-dark
hljs_languages:
- yaml
- rust
- go
navigation_depth: 2
shortcuts:
help: 191 # ?
next: 78 # n
previous: 80 # p
search: 83 # s
analytics:
gtag: G-XXXXXXXXXXSupported Locales: en, de, es, fa, fr, id, it, ja, nb, nl, nn, pl, pt_BR, ru, tr, uk, zh_CN, zh_TW
readthedocs Theme
Classic documentation theme from Read the Docs.
theme:
name: readthedocs
locale: en
highlightjs: true
hljs_languages:
- yaml
include_homepage_in_sidebar: true
prev_next_buttons_location: bottom # bottom, top, both, none
navigation_depth: 4
collapse_navigation: true
titles_only: false
sticky_navigation: true
logo: img/logo.png
analytics:
gtag: G-XXXXXXXXXX
anonymize_ip: trueLimitations:
- Only 2 levels of navigation in sidebar
- Limited customization options
Material for MkDocs (Third-Party)
Most popular third-party theme with extensive features.
Installation
pip install mkdocs-materialBasic Configuration
theme:
name: material
palette:
primary: indigo
accent: indigo
font:
text: Roboto
code: Roboto Mono
logo: assets/logo.png
favicon: assets/favicon.png
language: enColor Palette
theme:
name: material
palette:
# Light mode
- scheme: default
primary: indigo
accent: indigo
toggle:
icon: material/brightness-7
name: Switch to dark mode
# Dark mode
- scheme: slate
primary: indigo
accent: indigo
toggle:
icon: material/brightness-4
name: Switch to light modeNavigation Features
theme:
name: material
features:
- navigation.tabs # Top navigation tabs
- navigation.tabs.sticky # Sticky tabs
- navigation.sections # Expandable sections
- navigation.expand # Expand all by default
- navigation.path # Breadcrumbs
- navigation.top # Back to top button
- navigation.indexes # Section index pages
- navigation.instant # Instant loading
- navigation.tracking # Anchor tracking
- toc.integrate # Integrate TOC in nav
- toc.follow # Auto-scroll TOCSearch Features
theme:
name: material
features:
- search.suggest # Search suggestions
- search.highlight # Highlight matches
- search.share # Share searchCode Features
theme:
name: material
features:
- content.code.copy # Copy button
- content.code.annotate # Code annotations
- content.tabs.link # Link content tabsFull Material Configuration
theme:
name: material
custom_dir: overrides
language: en
palette:
- scheme: default
primary: indigo
accent: indigo
toggle:
icon: material/brightness-7
name: Switch to dark mode
- scheme: slate
primary: indigo
accent: indigo
toggle:
icon: material/brightness-4
name: Switch to light mode
font:
text: Roboto
code: Roboto Mono
logo: assets/logo.png
favicon: assets/favicon.png
icon:
repo: fontawesome/brands/github
features:
- navigation.tabs
- navigation.sections
- navigation.expand
- navigation.path
- navigation.top
- navigation.indexes
- navigation.instant
- search.suggest
- search.highlight
- content.code.copy
- content.code.annotate
- content.tabs.linkTheme Customization
Using Extra CSS/JS
theme:
name: mkdocs
extra_css:
- css/extra.css
extra_javascript:
- js/extra.jsdocs/css/extra.css:
:root {
--md-primary-fg-color: #1a73e8;
}
.md-header {
background-color: var(--md-primary-fg-color);
}Custom Theme Directory
Override theme files without modifying the original.
theme:
name: mkdocs
custom_dir: custom_theme/Directory Structure:
custom_theme/
├── css/
│ └── extra.css
├── js/
│ └── extra.js
├── img/
│ └── favicon.ico
├── 404.html
└── main.htmlTemplate Block Overrides
Create main.html to extend base template:
{% extends "base.html" %}
{% block htmltitle %}
<title>Custom Title - {{ page.title }}</title>
{% endblock %}
{% block content %}
{{ super() }}
<div class="custom-section">
Custom content here
</div>
{% endblock %}
{% block footer %}
{{ super() }}
<script>console.log("Footer loaded");</script>
{% endblock %}Available Blocks:
| Block | Description |
|---|---|
site_meta | Meta tags in head |
htmltitle | Page title |
styles | Stylesheet links |
libs | JavaScript libraries |
scripts | JavaScript after page load |
analytics | Analytics scripts |
extrahead | Custom content in head |
site_name | Site name in nav |
site_nav | Navigation |
search_button | Search box |
next_prev | Next/Previous buttons |
repo | Repository link |
content | Page content |
footer | Page footer |
Custom 404 Page
Create custom_theme/404.html:
<!DOCTYPE html>
<html>
<head>
<title>Page Not Found</title>
</head>
<body>
<h1>404 - Page Not Found</h1>
<p>The page you're looking for doesn't exist.</p>
<a href="{{ base_url }}">Go to Homepage</a>
</body>
</html>Template Variables
Global Variables
| Variable | Description |
|---|---|
config | MkDocsConfig object |
nav | Navigation structure |
base_url | Relative path to root |
mkdocs_version | MkDocs version |
build_date_utc | Build timestamp |
pages | All File objects |
page | Current page object |
Page Object
| Attribute | Description |
|---|---|
page.title | Page title |
page.content | Rendered HTML |
page.toc | Table of contents |
page.meta | Page metadata |
page.url | Relative URL |
page.abs_url | Absolute URL |
page.canonical_url | Full canonical URL |
page.edit_url | Edit on GitHub link |
page.is_homepage | Boolean |
page.previous_page | Previous page object |
page.next_page | Next page object |
Template Filters
| Filter | Description |
|---|---|
url | Normalize URLs |
tojson | Convert to JSON |
script_tag | Generate script tag |
Usage:
<link href="{{ 'css/extra.css' | url }}" rel="stylesheet">
<script>var data = {{ config.extra | tojson }};</script>
{{ 'js/extra.js' | script_tag }}Creating Custom Themes
Minimal Theme Structure
my-theme/
├── main.html # Required - main template
└── mkdocs_theme.yml # Theme configurationmain.html:
<!DOCTYPE html>
<html>
<head>
<title>{% if page.title %}{{ page.title }} - {% endif %}{{ config.site_name }}</title>
{% for path in config.extra_css %}
<link href="{{ path | url }}" rel="stylesheet">
{% endfor %}
</head>
<body>
<nav>
{% for nav_item in nav %}
<a href="{{ nav_item.url | url }}">{{ nav_item.title }}</a>
{% endfor %}
</nav>
<main>
{{ page.content }}
</main>
{% for script in config.extra_javascript %}
{{ script | script_tag }}
{% endfor %}
</body>
</html>mkdocs_theme.yml:
extends: null
locale: en
static_templates:
- 404.html
include_search_page: true
search_index_only: falsePackaging for Distribution
Project Structure:
mkdocs-mytheme/
├── setup.py
├── MANIFEST.in
├── README.md
└── mkdocs_mytheme/
├── __init__.py
├── mkdocs_theme.yml
├── main.html
├── css/
│ └── style.css
└── js/
└── app.jssetup.py:
from setuptools import setup
setup(
name='mkdocs-mytheme',
version='1.0.0',
packages=['mkdocs_mytheme'],
include_package_data=True,
entry_points={
'mkdocs.themes': [
'mytheme = mkdocs_mytheme',
]
}
)MANIFEST.in:
recursive-include mkdocs_mytheme *.html *.css *.js *.ymlPopular Third-Party Themes
| Theme | Description |
|---|---|
| Material | Feature-rich, modern design |
| Cinder | Clean, sidebar navigation |
| Windmill | Light/dark mode support |
| ReadTheDocs Dropdown | Enhanced ReadTheDocs |
| GitBook | GitBook-style layout |
| Bootstrap4 | Bootstrap framework |
| Ivory | Minimal, clean design |
| Terminal | Terminal/CLI aesthetic |
| Dracula | Dark theme with Dracula colors |
Install via pip:
pip install mkdocs-material
pip install mkdocs-cinder
pip install mkdocs-windmillTheme Localization
Enable i18n Support
pip install 'mkdocs[i18n]'Configure Locale
theme:
name: mkdocs
locale: frNotes:
- Theme translations only affect UI elements (Next, Previous, Search, etc.)
- Content translation requires separate i18n plugin
- Locales use ISO 639-1 codes (en, de, fr, etc.)
MkDocs Versioning Guide
Complete guide to managing multiple documentation versions with mike and other tools.
mike - Multi-Version Documentation
mike is the standard tool for deploying multiple documentation versions to GitHub Pages.
Installation
pip install mikeHow mike Works
mike deploys documentation versions to separate directories on your gh-pages branch:
gh-pages/
├── 1.0/
│ └── (docs for v1.0)
├── 1.1/
│ └── (docs for v1.1)
├── latest/
│ └── (alias pointing to 1.1)
├── versions.json
└── index.html (redirect)Basic Usage
Deploy a Version
# Deploy current docs as version 1.0
mike deploy 1.0
# Deploy and set as default alias
mike deploy 1.0 latest
# Update latest to point to new version
mike deploy 1.1 latest --update-aliasesAliases
Aliases are symbolic names that point to versions:
# Create alias "latest" pointing to 1.0
mike alias 1.0 latest
# Create alias "stable" pointing to 1.0
mike alias 1.0 stable
# Update "latest" to point to 1.1
mike alias 1.1 latest --updateSet Default Version
# Set "latest" as the default (shown at root URL)
mike set-default latest
# Set specific version as default
mike set-default 1.0List Versions
# Show all deployed versions
mike listOutput:
1.1 [latest]
1.0 [stable]
0.9Delete Versions
# Delete a specific version
mike delete 0.9
# Delete all versions
mike delete --allLocal Preview
# Serve all versions locally
mike serveAccess at http://localhost:8000 - navigate between versions.
Configuration
mkdocs.yml Setup
site_url: https://username.github.io/project/
theme:
name: material
extra:
version:
provider: mike
default: latestVersion Selector (Material Theme)
The Material theme automatically shows a version selector when version.provider: mike is configured.
theme:
name: material
extra:
version:
provider: mike
default: latest
alias: true # Show aliases in selectorCustom Version Warning
Add a banner for old versions:
# mkdocs.yml
extra:
version:
provider: mike
default: latestdocs/overrides/main.html:
{% extends "base.html" %}
{% block announce %}
{% if config.extra.version %}
{% set versions = config.extra.versions | default([]) %}
{% if version != "latest" %}
<div class="md-banner">
<div class="md-banner__inner md-grid md-typeset">
You're viewing an old version.
<a href="{{ config.site_url }}latest/">View latest</a>
</div>
</div>
{% endif %}
{% endif %}
{% endblock %}CI/CD Integration
GitHub Actions - On Release
# .github/workflows/docs.yml
name: Deploy Docs
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
pip install mkdocs-material mike
- name: Configure Git
run: |
git config user.name github-actions
git config user.email github-actions@github.com
- name: Get version
id: version
run: echo "version=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
- name: Deploy docs
run: |
mike deploy ${{ steps.version.outputs.version }} latest --update-aliases --pushGitHub Actions - On Push to Main
For dev/nightly documentation:
name: Deploy Dev Docs
on:
push:
branches:
- main
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- run: pip install mkdocs-material mike
- name: Configure Git
run: |
git config user.name github-actions
git config user.email github-actions@github.com
- name: Deploy dev docs
run: mike deploy dev --pushCombined Workflow
name: Documentation
on:
push:
branches:
- main
tags:
- 'v*'
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- run: pip install mkdocs-material mike
- name: Configure Git
run: |
git config user.name github-actions
git config user.email github-actions@github.com
- name: Deploy version
run: |
if [[ "$GITHUB_REF" == refs/tags/v* ]]; then
VERSION="${GITHUB_REF#refs/tags/v}"
mike deploy "$VERSION" latest --update-aliases --push
else
mike deploy dev --push
fiVersion Strategies
Semantic Versioning
Deploy each minor version:
# Major versions
mike deploy 1.0 --push
mike deploy 2.0 --push
# With minor versions
mike deploy 1.0.0 --push
mike deploy 1.0.1 --push
mike deploy 1.1.0 --pushMajor Version Only
Keep only major versions to reduce clutter:
mike deploy 1 latest --update-aliases --push
mike deploy 2 latest --update-aliases --pushNamed Releases
Use meaningful aliases:
mike deploy 1.0 stable --push
mike deploy 1.1 beta --push
mike deploy main dev --pushBranch-Based
Deploy docs from different branches:
# On main branch
mike deploy dev --push
# On release/1.0 branch
mike deploy 1.0 stable --push
# On release/2.0 branch
mike deploy 2.0 latest --pushAdvanced Configuration
Custom Branch
Deploy to a different branch:
mike deploy 1.0 --branch docs-versions --push# mkdocs.yml
remote_branch: docs-versionsCustom Remote
Deploy to a different remote:
mike deploy 1.0 --remote upstream --pushPrefix Path
For project pages with subdirectory:
# mkdocs.yml
site_url: https://org.github.io/project/Version Warning Script
Add a script to warn users on old versions:
docs/javascripts/version-warning.js:
document.addEventListener("DOMContentLoaded", function() {
var defined = document.body.dataset.mdVersion !== undefined;
var version = document.body.dataset.mdVersion;
if (defined && version !== "latest" && version !== "dev") {
var banner = document.createElement("div");
banner.className = "version-warning";
banner.innerHTML =
"This is documentation for version " + version + ". " +
"<a href='/latest/'>View the latest version</a>.";
document.body.insertBefore(banner, document.body.firstChild);
}
});# mkdocs.yml
extra_javascript:
- javascripts/version-warning.jsAlternative: mkdocs-versioning
Alternative plugin for version management.
Installation
pip install mkdocs-versioningConfiguration
plugins:
- versioning:
version: 1.0
version_selector: true
exclude:
- changelog.mdAlternative: Manual Versioning
For simple cases, manage versions manually:
Directory Structure
docs-repo/
├── v1/
│ ├── mkdocs.yml
│ └── docs/
├── v2/
│ ├── mkdocs.yml
│ └── docs/
└── build-all.shBuild Script
#!/bin/bash
# build-all.sh
for version in v1 v2; do
cd "$version"
mkdocs build --site-dir "../site/$version"
cd ..
done
# Create redirect at root
echo '<meta http-equiv="refresh" content="0; url=v2/">' > site/index.htmlTroubleshooting
Version Not Showing
1. Check versions.json exists on gh-pages:
git checkout gh-pages
cat versions.json2. Verify mike configuration:
extra:
version:
provider: mikeAlias Not Updating
Use --update-aliases flag:
mike deploy 1.1 latest --update-aliases --pushBroken Links Between Versions
Use relative links in documentation:
[See configuration](../configuration.md)Avoid absolute paths that include version:
<!-- Bad -->
[Docs](/1.0/configuration/)
<!-- Good -->
[Docs](configuration.md)Local Preview Issues
Ensure you're serving with mike:
# Correct
mike serve
# Wrong (won't show version selector)
mkdocs serveCommand Reference
| Command | Description |
|---|---|
mike deploy VERSION [ALIAS...] | Deploy docs as VERSION |
mike alias VERSION ALIAS | Create/update alias |
mike retitle VERSION TITLE | Change version title |
mike set-default VERSION | Set default redirect |
mike delete VERSION | Delete a version |
mike list | List all versions |
mike serve | Serve locally |
Common Flags:
--push- Push to remote after deploy--update-aliases- Update existing aliases--branch BRANCH- Use different branch--remote REMOTE- Use different remote--prefix PREFIX- Add URL prefix
Best Practices
1. Use Aliases - Always use latest alias for current stable 2. Automate Deploys - Use CI/CD for consistent deployments 3. Version Warning - Show banner on old versions 4. Clean Up - Delete very old versions periodically 5. Semantic Names - Use latest, stable, dev aliases 6. Test Locally - Preview with mike serve before deploying