Skip to main content

GitHub Flavored Markdown

import { GfmMarkdown } from '@react-markdown-kit/renderer/gfm'

<GfmMarkdown>{content}</GfmMarkdown>

GFM adds tables, task lists, strikethrough, autolinks and footnotes on top of CommonMark. It is off by default, because CommonMark is the smaller promise.

Two routes

The native extension. gfm() is a Markdown extension, so it also configures the editor and the template engine through a shared preset.

import Markdown, { defineMarkdownPreset, gfm } from '@react-markdown-kit/renderer'

const appMarkdown = defineMarkdownPreset({ extensions: [gfm()] })

<Markdown preset={appMarkdown}>{content}</Markdown>

The remark plugin. The plugin you already use keeps working.

import Markdown from '@react-markdown-kit/renderer'
import remarkGfm from 'remark-gfm'

<Markdown remarkPlugins={[remarkGfm]}>{content}</Markdown>

The two routes are fixture-tested to agree. tests/gfm.test.ts runs the same GFM corpus through both and compares the rendered HTML, so the plugin route is not a second-class path.

Pick the extension when you want one dialect shared across the kit. Pick the plugin when you are migrating and want to change as little as possible.

GfmMarkdown is the shorthand for the extension route, and takes every prop Markdown takes.

Strikethrough with one tilde

gfm() takes one option.

gfm({ singleTilde: false })

The default is true, which is what GitHub, cmark-gfm and remark-gfm all do. With the default, ~x~ is struck through. Set it to false to require ~~x~~.

Tables

Tables, with per-column alignment
Markdown (edit me)
Rendered
PackageRoleSize
rendererdisplay24 kB
editorcreatelarger
templatepersonalizesmall

The second row sets alignment. :--- is left, :---: is centre, ---: is right.

A cell is an ordinary Markdown subtree, so emphasis, links and code work inside one.

A cell is not a special dialect
Markdown
| Field | Default |
| --- | --- |
| `skipHtml` | **true** |
| `urlTransform` | [defaultUrlTransform](https://example.com) |
Rendered
FieldDefault
skipHtmltrue
urlTransformdefaultUrlTransform

Task lists

Task lists
Markdown (edit me)
Rendered
  • Parse with micromark
  • Keep mdast as the document
  • Ship the migration codemod
    • Nested items work too

The checkboxes render disabled, because rendered Markdown is not a form. The editor makes them clickable.

The list carries contains-task-list and each item carries task-list-item, matching what GitHub and remark-gfm emit. Those are the only classes the renderer adds without being asked.

Strikethrough

Strikethrough
Markdown
Status: ~~blocked~~ shipped.

A single tilde ~also works~ by default.
Rendered

Status: blocked shipped.

A single tilde also works by default.

A bare URL becomes a link with no angle brackets and no link syntax.

Literal autolinks
Markdown
Read https://example.com/spec for details.

Mail support@example.com with questions.

Angle brackets still work: <https://example.com>
Rendered

Read https://example.com/spec for details.

Mail support@example.com with questions.

Angle brackets still work: https://example.com

URL policy still applies to an autolink. An unsafe scheme is emptied before it reaches the DOM. See Security.

Footnotes

Footnotes, with a generated back-reference
Markdown (edit me)
Rendered

The parser is micromark1, not a set of regular expressions.

Footnotes

  1. Line-oriented matching cannot see block structure, which is where the old corruptions came from.

Numbering is derived from document order. The footnote section is rendered last, whatever order the definitions appear in.

A footnote definition is a block, so it can hold paragraphs, lists and code.

Everything at once

One document, every GFM feature
Markdown (edit me)
Rendered

Weekly report

Owner: https://example.com/team

TaskOwnerDone
ImporterAdayes
ExporterGraceno
  • Land the parser1
  • Land the writer

Footnotes

  1. Shipped on Tuesday.

What GFM does not turn on

Raw HTML stays inert. GFM does not change the security policy, and skipHtml still defaults to true.

Heading anchors, emoji shortcodes and mentions are GitHub website features, not GFM syntax. Add them with a remark or rehype plugin.

Bundle gfm() with your components and policy in a preset, then reuse it in the editor.

The editor understands every GFM construct on this page, and writes them back unchanged. See Round-trip preservation.