Native Markdown

Write wiki pages in Markdown, the widely used markup format and the native language of AI agents, rendered as real wiki pages

On this page

External links

Native Markdown for MediaWiki

Native Markdown makes Markdown a native MediaWiki content model. Whole pages are stored as Markdown and rendered as real wiki pages, with working links, categories, templates, a table of contents, and full-text search. Install it and new pages default to Markdown across the whole wiki, while existing pages stay as they are.

Professional Wiki created Native Markdown. We are MediaWiki developers that provide professional MediaWiki services.

Usage Documentation

The whole page is Markdown, rendered as a real wiki page.

Markdown as a content model

The content model determines the format in which MediaWiki stores and renders a page. Native Markdown adds Markdown as a content model, so a Markdown page is not wikitext with Markdown embedded inside it. The whole page is Markdown, stored exactly as you typed it, and rendered as a real wiki page. Markdown pages and wikitext pages coexist on the same wiki, and you choose the model per page.

A Markdown page rendered as a normal wiki page, with a sidebar table of contents, an embedded diagram, and a styled table

Wikitext link syntax works inside Markdown, with the semantics you know. [[Page]] gives you a blue or red link depending on whether the page exists, and the link is tracked in Special:WhatLinksHere and Special:WantedPages. [[Category:X]] assigns a category. [[File:Diagram.png|300px|alt=Text|Caption]] embeds an image. Section links, same-page anchors, and interwiki links all behave like their wikitext counterparts. The table of contents is the real MediaWiki table of contents, built from your Markdown headings.

A Markdown page demonstrating wiki links, an embedded diagram, a wikitext comparison table, and task lists
Syntax Effect
[[Page]], [[Namespace:Page]] Internal link, blue or red by page existence, tracked in Special:WhatLinksHere
[[Page|label]] Internal link with label
[[Page#Section]], [[#Section]] Link to a section of another page or of the current page
[[Category:X]] Assigns the category and renders nothing, like wikitext
[[Category:X|sort key]] Assigns with a sort key
[[:Category:X]] Visible link to the category page
[[File:X.png]] Embeds the file at full size; missing files render as an upload link
[[File:X.png|300px|alt=Alt text|Caption]] Embed with width, alt text and caption (tooltip); each parameter is optional
[[File:X.png|thumb|Caption]] Framed thumbnail with the caption visible below the image, like wikitext thumbs
[[:File:X.png]] Link to the file page instead of embedding
[[wikipedia:Page]] Interwiki link, using the wiki's interwiki table
#REDIRECT [[Page]] On the first line, makes the page a redirect, and a page move writes one automatically

Anything inside [[...]] that is not a valid title renders as literal text.

A few behaviors differ from wikitext links:

  • Inside GFM table cells, | separates columns before links are parsed, so write [[Page\|label]] with a backslash escape: | [[Page\|label]] |.
  • Height-only image sizes (x100px) are ignored; only the width of 100px or 100x200px is used.
  • [[Media:X]] links land on the file description page rather than the raw file.
  • Relative subpage links ([[/sub]]) are not resolved; the text is treated as a literal page title.
  • [[Special:...]] links render but are not recorded in Special:WhatLinksHere, as in wikitext.

Templates and parser functions

You can include your wikitext templates, such as infoboxes, directly in Markdown pages with the usual {{...}} syntax. Expansion runs through MediaWiki's own parser, so template dependencies are tracked (editing a template re-renders the pages that use it), and the output is sanitized exactly as wikitext is.

A Markdown page with a rendered service infobox template floating on the right

Parser functions and magic words work inside the braces too, whether built into MediaWiki, like {{PAGENAME}}, or added by extensions, like {{#if:}}. Headings inside templates stay out of the table of contents.

Where you place a call decides whether its output is block or inline, and how the braces are read:

  • A call on its own line produces block output, so an infobox renders as a block rather than being wrapped in a paragraph. It may span several lines, including blank parameter lines, until the braces balance.
  • A call within a line of text renders inline and has to stay on a single line. Multi-line calls must start on their own line.
  • Template arguments are wikitext rather than Markdown. Inside a GFM table cell, escape argument pipes as \|, and write \{\{ to keep braces literal.
  • A block call with no closing }} renders as literal text through to the end of the page, since Markdown parsing cannot backtrack, so a forgotten brace shows up as visible braces to fix rather than a silent error.

The page source stays clean Markdown regardless: action=raw returns the literal {{...}} you typed rather than the expanded output, so the round trip for AI agents and git stays lossless.

A few less common things are out of scope in this version:

  • <ref> and <references/> in the Markdown body, where footnotes cover citations instead. Citation state is not shared across separate template calls on a page.
  • Transcluding another Markdown page with {{:Page}}. Its source would be reinterpreted as wikitext, so transclude wikitext pages instead.
  • subst: does not substitute on save.

Transclusion and parser functions are on by default. Set $wgNativeMarkdownWikitextExpansion = false to turn them off and leave {{...}} as literal text. Turning the feature back on does not reparse pages that already exist. They show template output once they are next edited or purged, and running refreshLinks.php populates the template links eagerly.

Markdown flavor

The Markdown flavor is CommonMark plus GitHub Flavored Markdown: tables, strikethrough, task lists, and autolinks, with footnotes on top. Tables are automatically styled as wikitables. YAML front matter is parsed, hidden from the rendered page, and stored as page metadata. Raw HTML is always escaped, so page content cannot inject scripts or other active HTML into your wiki.

For readers coming from wikitext:

Wikitext Markdown
'''bold''', ''italic'' **bold**, *italic*
== Heading == ## Heading
* bullet / # numbered - bullet / 1. numbered
[https://example.org label] [label](https://example.org)
{| class="wikitable" ... GFM tables (| a | b |), styled as wikitable automatically
<syntaxhighlight lang="php"> fenced code block: ```php (see syntax highlighting)
<ref>Source</ref> footnote: [^1] plus [^1]: Source
[[Page]], [[Category:X]], [[File:X.png]] identical (see wiki links)
{{Template}}, parser functions, magic words transcluded through MediaWiki's parser (see Templates)

MediaWiki already renders the page title as the top-level heading, so starting a page with a # Heading is optional. When present, it becomes the first entry of the table of contents.

Syntax highlighting

Fenced code blocks that name a language are syntax highlighted, the same way a wikitext <syntaxhighlight> block is, so code looks the same on Markdown and wikitext pages. This needs the SyntaxHighlight extension bundled with MediaWiki to be enabled. Without it, and for blocks that do not name a language the highlighter recognizes, the code stays plain preformatted text.

A block like this:

```python
def fetch_markdown(title: str) -> str:
    """Return the page's stored Markdown, byte for byte."""
    response = requests.get(f"{WIKI}/index.php", params={"title": title, "action": "raw"})
    response.raise_for_status()
    return response.text
```

renders as:

The Python code block from the example, rendered with syntax highlighting

Editing

Editing happens in the standard edit form. With the CodeEditor extension installed you get Markdown syntax highlighting, and Show preview renders through the full pipeline. Diffs, history, undo, and rollback work as they do for wikitext pages, comparing and reverting the stored Markdown line by line. Edit conflicts are merged three-way at the Markdown source level, and genuinely conflicting edits surface the normal conflict screen.

The MediaWiki edit form with Markdown syntax highlighting

Full-text search indexes the rendered prose of a Markdown page rather than the raw markup, so keyword lookups match what a reader sees. Search snippets are clean text instead of # and ** noise, and front matter stays out of the index. The index updates through the job queue when a page is edited, so pages saved before Native Markdown was installed or upgraded keep their previously indexed text until their next edit or a search index rebuild.

MediaWiki search results showing a clean text snippet from a Markdown page, without markup symbols

Built for AI agents

Markdown is the format that AI assistants like Claude and ChatGPT read and write best. Because Native Markdown stores pages as plain Markdown, an agent can read and edit them directly.

Fetching a page with action=raw returns the Markdown source exactly as it was saved, front matter and all. The REST API (/rest.php/v1/page/{title}) reports content_model: markdown alongside the source, so an agent knows what kind of page it is working with. When an agent wants resolved links and the table of contents instead of the source, action=parse returns the rendered HTML. Writing works through the ordinary editing APIs, or more conveniently through the MediaWiki MCP Server, the tool that lets AI assistants read and edit your wiki. The round trip is lossless: an agent can fetch a page, edit it, and write it back without the content drifting through a wikitext translation. The same property makes Markdown pages a good fit for round-trips with git repositories.

Current limitations

Development so far has focused on making the content model itself solid. A couple of MediaWiki editing features are not there yet: there is no VisualEditor support, and no section editing.

If your editors rely on VisualEditor, wikitext remains the right model for those pages, and both models coexist on the same wiki, so you choose per page. On the roadmap: mapping front matter to structured data such as Semantic MediaWiki properties, live preview and WYSIWYG editing, and import tooling for Obsidian vaults and git repositories.

Comparison with other Markdown extensions

Native Markdown exists because no other maintained MediaWiki extension makes Markdown a native content model. The alternatives embed Markdown inside wikitext pages or offer a thin content handler on top of a generic Markdown parser, so the Markdown never becomes a first-class wiki page.

  • Extension:WikiMarkdown embeds Markdown blocks inside wikitext pages through a tag, plus a shallow .md content handler built on Parsedown. Inside that Markdown there are no working [[wiki links]], no category assignment, and no MediaWiki table of contents. Native Markdown makes the whole page Markdown, so links, categories, the table of contents, full-text search, and the link tables all behave the way they do on wikitext pages.
  • Extension:Markdown is archived and points visitors to WikiMarkdown.
  • MarkdownExtraParser has been unmaintained for over a decade.

Related but different is our own ExternalContent extension, which embeds Markdown files from external sources such as GitHub into wikitext pages. Native Markdown is instead for the wiki's own pages being Markdown, and the two compose nicely.

Installation

Requirements

  • PHP 8.1 or later
  • MediaWiki 1.43 or later

Load and enable

Load the extension using Composer:

COMPOSER=composer.local.json composer require --no-update professional-wiki/native-markdown:~1.0

Enable the extension by adding the following to your LocalSettings.php:

wfLoadExtension( 'NativeMarkdown' );

You can verify the installation by checking your wiki's Special:Version page.

Native Markdown is also available via our managed MediaWiki hosting, where you turn it on from the Admin Panel.

The Markup language setting in the ProWiki Admin Panel, offering wikitext only, .md pages as Markdown, or Markdown by default

Configuration

Markdown is on by default. Once the extension is installed, new pages default to the Markdown content model across the whole wiki, with deliberate exceptions where wikitext is load-bearing: the discussion (Talk) namespaces, where signatures and threading depend on wikitext; the Template and MediaWiki namespaces; and any namespace with an explicitly configured content model, for example Scribunto modules or JSON pages. Titles ending in .css, .js, or .json never default to Markdown either, since MediaWiki reserves those for code pages. The default affects page creation only. Existing pages never change model, and individual pages can be switched between wikitext and Markdown, in both directions, via Special:ChangeContentModel.

External links on Markdown pages honor the core $wgNoFollowLinks setting, and input size is bounded by the core $wgMaxArticleSize limit, just as on wikitext pages.

To keep wikitext as the default, set $wgNativeMarkdownEverywhere = false; in LocalSettings.php. To offer Markdown only in specific namespaces, list them in $wgNativeMarkdownNamespaces. With $wgNativeMarkdownSuffixDetection, a title ending in .md is read as a deliberate per-page choice, so such new pages default to Markdown and both models can mix by naming convention. Because the suffix is a deliberate choice, this mode also applies inside the Talk namespaces, unlike the wiki-wide default, and a Markdown talk page created this way has no wikitext signatures or threading. It still skips the Template and MediaWiki namespaces, where Markdown can act as neither a template nor an interface message. Add those to $wgNativeMarkdownNamespaces to opt them in anyway. See the configuration reference for the full list of settings.

Converting existing pages

Activation modes apply at page creation only, so enabling suffix detection or adding a namespace never touches pages that already exist. The NativeMarkdown:ConvertToMarkdownModel maintenance script is the retroactive counterpart. It switches existing pages to the Markdown model using the same rules, changing the stored content model rather than the page text. The stored wikitext is then reinterpreted as Markdown and may render differently, so always start with --dry-run to review what would be converted. The script suits pages whose text already is Markdown, such as files imported from a git repository before Native Markdown was installed.

Two selectors choose the pages, and at least one is required:

  • --md-suffix follows live .md suffix detection: titles ending in .md, in any namespace except Template and MediaWiki, Talk included.
  • --namespace <id> follows $wgNativeMarkdownNamespaces and converts every page in that namespace. An explicit namespace is a deliberate choice, so it works for any namespace, Template and MediaWiki included.

Combined, the two narrow to the .md-titled pages inside a single namespace, so --md-suffix --namespace 10 converts the .md pages in the Template namespace that --md-suffix alone would skip. Only pages whose current model is wikitext are ever converted, other models are left untouched, and redirects are skipped. Use --batch-size to control how many pages each batch processes.

php maintenance/run.php NativeMarkdown:ConvertToMarkdownModel --md-suffix --dry-run
php maintenance/run.php NativeMarkdown:ConvertToMarkdownModel --namespace 3000
php maintenance/run.php NativeMarkdown:ConvertToMarkdownModel --md-suffix --namespace 10

Each conversion is an ordinary revision by the maintenance user, visible in page history and reversible per page with Special:ChangeContentModel, which stays the tool for one-off, two-way model changes.

More MediaWiki Extensions

View all our extensions

Professional MediaWiki Services

MediaWiki services by the authors of Native Markdown