Technical previewShare feedback

Search

Search documentation

Recipes

Updated Nov 1, 2026

Markdown to HTML, as GitHub renders it

GFM syntax with its alerts, headings that link to themselves and colored code:

from textlane import html, md, textlane

file = (
    textlane()
    .use(md.parse())
    .use(md.gfm({"alerts": True}))
    .use(md.to_html())
    .use(html.slug({"autolink": {"behavior": "wrap"}}))
    .use(html.highlight({"theme": "github-dark"}))
    .use(html.stringify())
    .process("## Install\n\n> [!TIP]\n> Use pnpm.\n\n```sh\nnpm install textlane\n```")
)
file["value"]
# <h2 id="install"><a href="#install">Install</a></h2>
# <div class="markdown-alert markdown-alert-tip" dir="auto">
# …
# <pre class="shiki github-dark" style="background-color:#24292e;color:#e1e4e8" …
# …

Untrusted Markdown to safe HTML

Keep the raw HTML a user wrote, then let html.sanitize remove whatever GitHub's schema does not allow:

from textlane import html, md, textlane

file = (
    textlane()
    .use(md.parse())
    .use(md.to_html({"allowDangerousHtml": True}))
    .use(html.raw())
    .use(html.sanitize())
    .use(html.stringify())
    .process("Hi <img src=x onerror=alert(1)>")
)
file["value"]
# <p>Hi <img src="x"></p>

HTML to Markdown

from textlane import html, md, textlane

file = (
    textlane()
    .use(html.parse({"fragment": True}))
    .use(html.to_markdown())
    .use(md.gfm())
    .use(md.stringify())
    .process("<h1>Notes</h1><ul><li><s>old</s></li><li>new</li></ul>")
)
file["value"]
# # Notes
#
# * ~~old~~
# * new

Formatting Markdown

Parse and stringify with the markers you prefer:

from textlane import md, textlane

file = (
    textlane()
    .use(md.parse())
    .use(md.stringify({"bullet": "-", "emphasis": "_", "strong": "*"}))
    .process("* *a*\n* __b__")
)
file["value"]
# - _a_
# - **b**

MDX to JavaScript

from textlane import md, mdx, textlane

file = (
    textlane()
    .use(md.parse())
    .use(md.mdx())
    .use(md.to_html())
    .use(mdx.compile())
    .process("# Hi {props.name}")
)
file["value"]
# import {jsx as _jsx} from "react/jsx-runtime";
# …

Checking prose in Markdown

The checks report messages and leave the text alone; each message names its rule and where it is:

from textlane import md, nl, textlane

file = (
    textlane()
    .use(md.parse())
    .use(md.to_natural_language())
    .use(nl.passive())
    .use(nl.indefinite_article())
    .use(nl.stringify())
    .process("# Notes\n\nThe build was broken by a update.")
)
report = "\n".join(
    f"{m['place']['start']['line']}:{m['place']['start']['column']} {m['reason']}"
    for m in file["messages"]
)
report
# 3:15 Unexpected use of the passive voice
# 3:25 Unexpected article `a` before `update`, expected `an`

Created with ❤ and Livemark