Migrating from Material for MkDocs

This content was generated with the assistance of AI and should be verified against the official documentation of each tool before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page was written against Zensical 0.0.72 (PyPI, published 2026-10-23, alpha status) and Material for MkDocs 9.8.0, and checked on 2026-10-26. Zensical is built by the team behind Material for MkDocs and reads an existing mkdocs.yml, so a migration can be a series of small, reversible steps instead of a rewrite. The examples use the small Pocket Units documentation project (a units-conversion library) that appears in the other Zensical pages.

Why migrate, and why not in one step

Material for MkDocs no longer receives new features. According to the Zensical announcement, only critical bug fixes and security updates continue, and the end-of-life date is 2027-05-05 (extended by six months at the 2026-11-05 launch, according to the upcoming changes page). The announcement also describes MkDocs, unmaintained since August 2024, as a supply chain risk. Building Material for MkDocs 9.8.0 prints this end-of-life notice and a second warning, printed by a dependency, recommending a third-party continuation of MkDocs called ProperDocs. The first notice comes from the Material team and points to Zensical. The second does not come from the Material team, and the official migration guide does not recommend it; this page does not evaluate it.

The official guide is built around a safety net: Zensical reads the same mkdocs.yml, so the same folder can be built by either command, and the MkDocs build keeps working until you decide to drop it. A migration is therefore not a conversion project. It is an evaluation that ends with replacing mkdocs by zensical in local and CI commands.

Gradual and reversible migration

The recommended order, in the words of the official guide, is to keep the MkDocs production deployment unchanged, build the same project with Zensical using the existing mkdocs.yml, compare representative pages, navigation, search and customizations, check that every plugin you depend on is supported, and switch local and CI commands only when you trust the result.

One mkdocs.yml built by MkDocs and by Zensical, producing a production site and a preview site that are compared
Figure 1. Reading mkdocs.yml with both builders

The figure shows the evaluation set-up. Both builders read the same configuration and Markdown folder. The production site keeps coming from MkDocs; the Zensical output goes to a different folder and is only reviewed.

A worked example

The Pocket Units project is a Material for MkDocs site. Its configuration deliberately contains the three things that Zensical does not support or ignores: a hook, the gen-files plugin and a variant key that MkDocs does not know.

mkdocs.yml
site_name: Pocket Units
site_url: https://example.org/pocket-units/
theme:
  name: material
  variant: classic
  palette:
    scheme: default
nav:
  - Home: index.md
  - Guide: guide.md
plugins:
  - search
  - tags
  - gen-files:
      scripts:
        - scripts/gen_pages.py
hooks:
  - hooks/shout.py
markdown_extensions:
  - admonition

The hook appends a marker to every page and the script writes one extra page, generated.md:

hooks/shout.py and scripts/gen_pages.py
# hooks/shout.py
def on_page_markdown(markdown, **kwargs):
    return markdown + "\n\nHOOK-MARKER\n"

# scripts/gen_pages.py
import mkdocs_gen_files

with mkdocs_gen_files.open("generated.md", "w") as f:
    f.write("# Generated\n\nGEN-MARKER\n")

Install both builders in one virtual environment and build twice. MkDocs gets an explicit output folder so that it does not collide with the site folder that Zensical writes:

python3.13 -m venv .venv
.venv/bin/pip install zensical==0.0.72 mkdocs-material==9.8.0 mkdocs-gen-files
.venv/bin/mkdocs build -d out-mkdocs
.venv/bin/zensical build --clean

Both commands finished. The Zensical run reported no problem:

Build started
No issues found
Build finished in 0.19s

Comparing the two outputs shows what the evaluation is for. The MkDocs output contains the hook marker in index.html and guide/index.html, and a generated/index.html page. The Zensical output contains neither: no marker and no generated page, and the sitemap lists two URLs instead of three. Zensical did not warn about either difference, because hooks is documented as unsupported and the configuration of plugins it does not list is silently ignored. Silence is the main risk of this approach, which is why the comparison step cannot be skipped: a clean Zensical build does not prove that everything the old build did still happens.

The search index also moves: MkDocs writes search/search_index.json, Zensical writes search.json at the root of the output. This matters only if something outside the site reads the index.

The classic variant

Zensical ships two theme variants, modern and classic. Both keep the same HTML structure. The classic variant keeps the appearance of Material for MkDocs and is the one the official guide recommends for an existing Material project, in particular when custom CSS or JavaScript depends on the established look. It is chosen with the variant key of the theme:

theme:
  name: material
  variant: classic

Three builds of the same project with the key absent, set to classic and set to modern show the practical effect. With theme.name: material and no variant the page links the stylesheets assets/stylesheets/modern/main.4ab2de0c.min.css and palette.b60ce83d.min.css, so the default is modern, not the Material look. With variant: classic the same page links assets/stylesheets/classic/main.94dd801d.min.css and palette.813d4b3f.min.css. A project that only changes the command from mkdocs to zensical therefore changes its appearance unless it adds the key. The hashes belong to 0.0.72 and change between releases.

MkDocs with Material 9.8.0 accepted the variant key without error (the build succeeded), so the key can stay in the shared mkdocs.yml while both builders are in use. With zensical.toml the same choice is written in the theme table:

[project.theme]
variant = "classic"

Configuration files

Zensical keeps the standard MkDocs layout: configuration at the project root, Markdown under docs_dir, output under site_dir. Page paths, directory URLs and the nav setting stay as they are. Moving to zensical.toml is not required and the guide says mkdocs.yml can stay for as long as you like. While both builders are in use, add new settings using the mkdocs.yml examples of the configuration reference so that both tools can read them.

Zensical Studio is described as becoming the place to assess compatibility and guide the final move to zensical.toml; that is a planned feature. There is no conversion command in the 0.0.72 command line, which has only new, build and serve. The equivalent of the example above, written by hand, is:

zensical.toml
[project]
site_name = "Pocket Units"
site_url = "https://example.org/pocket-units/"
nav = [{ "Home" = "index.md" }, { "Guide" = "guide.md" }]

[project.theme]
variant = "classic"

[project.plugins.tags]

[project.markdown_extensions.admonition]

This file built without issues and rendered the admonition from docs/index.md. Note that theme.name, the hook and gen-files have no counterpart in it. When both zensical.toml and mkdocs.yml were present in the same folder, 0.0.72 used zensical.toml (changing site_name there changed the page title); the official pages consulted do not state this precedence, so treat it as an observation. The -f option of zensical build selects a file explicitly, and zensical build -f mkdocs.yml used the YAML file even though a TOML file existed.

Gaps and replacements

Settings

Among the mkdocs.yml settings, the official guide lists only one as unsupported: hooks. A hook module that only adjusts content or navigation has no equivalent yet. Options are to move the logic out of the build (a script that runs before zensical build and edits files), to replace it with a supported plugin or Markdown extension, or to keep MkDocs for that project until support arrives. The official pages do not announce a date.

Command line flags and commands

The build and serve commands remain available, with these differences:

MkDocs Zensical Replacement

--theme

not supported; the 0.0.72 command prints No such option '--theme'

the variant key of the theme

--use-directory-urls

not supported; prints No such option '--use-directory-urls'

the use_directory_urls setting

--site-dir

not supported; prints No such option '--site-dir'

the site_dir setting

gh-deploy

not provided; prints No such command 'gh-deploy'

a publishing workflow, for example the GitHub Pages workflow created by zensical new

get-deps

not provided; prints No such command 'get-deps'

declare dependencies explicitly in pyproject.toml

The messages were produced by running the commands with Zensical 0.0.72. The options that exist are -f/--config-file, -c/--clean and -s/--strict for build. After an upgrade or when output looks stale, zensical build --clean is the recommended first step. The publishing page of the official site describes the GitHub Pages and GitLab Pages workflows that replace gh-deploy; see Validation and Publishing. A project that kept pushing a gh-pages branch with gh-deploy has to switch to the artifact-based Pages flow or write its own push step.

Plugins

Zensical does not run MkDocs plugins. It implements a list of them natively, so that the existing configuration keeps working. The compatibility page lists 31 native implementations: api-autonav, audio, autoapi, autorefs, awesome-nav, blog, callouts, exclude, gh-admonitions, glightbox, include-markdown, literate-nav, llmstxt, macros, markdown-exec, markdownextradata, meta, mike, minify, mkdocstrings, nav-weight, offline, privacy, redirects, rss, social, search, section-index, table-reader, tags and video. The text of that page says "30" while its list contains 31 names; the count here is the number of names listed.

Status Plugins and advice

Unsupported

gen-files: for API reference pages use autoapi or api-autonav; for other uses run the generation script separately from the build (the official page suggests uvx --with mkdocs-gen-files, which needs mkdocs.yml). open-in-new-tab: add its JavaScript with extra_javascript.

In progress

optimize

Planned

git-authors, git-committers, git-revision-date-localized (no release date implied)

Not listed

Configuration is silently ignored. The plugin is neither imported nor run.

Two rules shape the evaluation. First, settings of a plugin that is not listed vanish without any message, as the gen-files section of the worked example showed. Second, a supported plugin validates its settings: adding bogus_setting: true under tags made the Zensical build stop with Error: unknown tags option: bogus_setting, while the settings the official page marks as ignored are accepted silently. Custom plugins written for the MkDocs plugin API cannot run; the official page says a separate Module API is being prepared, available first in Zensical Spark. The mkdocstrings handler for Python still has to be installed (pip install mkdocstrings-python); see API Docs with mkdocstrings and Blog, Tags and Plugins.

Templates and overrides

Zensical renders templates with MiniJinja instead of Jinja. MiniJinja is largely compatible but has no Python interpreter and cannot call arbitrary Python functions, so templates must use the available filters and tests. Material for MkDocs adapted its own templates; the last required changes shipped in 9.6.18, and overrides based on that version or later should generally work. For older or heavily customised overrides the guide advises comparing them with the current Material templates, replacing Python function calls with filters or tests, and building a representative set of pages to check every overridden block. See Theming and Customization.

Markdown, versioning and other features

Python Markdown and Python Markdown Extensions are supported as in MkDocs, and popular third-party extensions are said to work unchanged; extra CSS and JavaScript also work with the classic variant. Versioning is the one area where the workflow differs: mike is listed among the natively implemented plugins, and the compatibility section has its own page about versioning. Read Versioning and i18n before migrating a site that publishes several versions.

Migration checklist

  1. Pin the versions in pyproject.toml or requirements.txt (zensical==0.0.72 and the current Material release) and install both builders in one virtual environment.

  2. Leave the MkDocs deployment untouched. Build with mkdocs build -d <other folder> and zensical build --clean.

  3. Add variant: classic under the material theme if the visual result must match, and rebuild.

  4. Search mkdocs.yml for hooks, plugins missing from the native list, --theme, --site-dir, --use-directory-urls, gh-deploy and get-deps in scripts and CI files.

  5. Compare the two outputs: file lists, sitemap entries, navigation, search, tags, redirects, generated pages, custom CSS and JavaScript, overridden templates. Use zensical build --strict once the first pass is clean.

  6. Replace each gap: workflow for gh-deploy, pyproject.toml for get-deps, autoapi or a pre-build script for gen-files, extra_javascript for open-in-new-tab.

  7. Switch local and CI commands from mkdocs to zensical and keep mkdocs.yml. Keep the old command available for a release cycle as the way back.

  8. Optionally move to zensical.toml when no builder other than Zensical needs mkdocs.yml.

flowchart TD A["Same mkdocs.yml, two builds"] --> B{"Hooks or unlisted plugins?"} B -- yes --> C["Replace, move out of the build or wait"] B -- no --> D["Set variant: classic"] C --> D D --> E["Compare outputs"] E --> F{"Equivalent?"} F -- no --> C F -- yes --> G["Switch CI and local commands"] G --> H["Keep mkdocs.yml, or move to zensical.toml"]

The diagram summarises the checklist as a loop: the switch happens only after the comparison finds no difference that matters, and each difference sends you back to a replacement.

Pitfalls and version notes

  • Zensical 0.0.x is alpha. The project announced that 0.1.0, planned for 2026-11-05, starts a dependable release line. Pin the version and read the changelog before upgrading; see Roadmap and Upgrade Policy.

  • A successful Zensical build with an unsupported hook or plugin shows no warning. Compare output instead of trusting the exit status.

  • The default variant is modern. Without variant: classic, a Material site changes its look.

  • Link validation is on by default in Zensical (see Validation and Publishing), so a first build may report broken links that MkDocs tolerated.

  • Search is only available in English in the Zensical interface.

  • In CI, caches are not recommended by the official publishing page, which is another difference from many MkDocs workflows.

  • Zensical Studio and Spark features (guided migration, Module API) are announced for the launch on 2026-11-05 and were not tried here.