Markdown syntax
Live cheatsheet of every markdown feature Publier ships out of the box — GFM (tables, task lists, strikethrough, autolinks, footnotes), frontmatter, directive callouts, and the raw-HTML escape hatch.
A live reference for everything the MDX pipeline understands without extra setup. Each section shows the source on the left of the code fence and the rendered result below.
Frontmatter
Every page starts with a YAML block. Astro reads it into the page’s content collection schema; Publier’s nav, sidebar, and SEO use these fields automatically.
---title: Page titledescription: 1–2 sentence summary used in search + meta description---Defined by remark-frontmatter (auto-wired by Astro’s MDX integration). Add custom fields freely — they survive into the rendered page’s frontmatter export.
GFM — tables
Pipe-delimited markdown tables, alignment via :--- / ---: / :---:.
| Feature | Status | Notes || ------------ | :------: | ---------------------- || Tables | shipped | this is one || Task lists | shipped | see below || Strikethrough| shipped | ~~deprecated stuff~~ || Footnotes | shipped | [^1] || Feature | Status | Notes |
|---|---|---|
| Tables | shipped | this is one |
| Task lists | shipped | see below |
| Strikethrough | shipped | |
| Footnotes | shipped | 1 |
GFM — task lists
Tick boxes inside list items.
- [x] Build the parser- [x] Wire up the renderer- [ ] Document the syntax- [ ] Ship to production- Build the parser
- Wire up the renderer
- Document the syntax
- Ship to production
GFM — strikethrough
~~tilde-wrapped~~ text renders with a line through it.
The old API was ~~mostly fine~~ unmaintainable.The old API was mostly fine unmaintainable.
GFM — autolinks
Bare URLs and email addresses get linked automatically — no need to wrap them in <...> or [...](...).
Read more at https://publier.net or mail us at [email protected].Read more at https://publier.net or mail us at [email protected].
Container directives
Triple-colon blocks parsed by remark-directive and rewritten into Publier’s <Aside> callouts. Four variants ship — note, tip, caution, danger. See the full reference at Callouts.
:::tipUse `bunx publier` to try a one-off command without installing the native CLI.:::Inline directives (:foo[label]{attr=val}) and leaf directives (::break{align=center}) are also parsed, but Publier ships handlers only for the four container types listed above. Any unhandled directive passes through as a plain HTML node — fine for inert markup, never for user input (see Raw HTML below).
Math (opt-in)
KaTeX, behind docsShell({ math: true }). Wrap inline in $...$ or block in $$...$$. Full demo at Math equations.
Raw HTML — what we DON’T do, and why
Publier intentionally does not wire rehype-raw into the pipeline. Raw HTML in MDX is a footgun: a <script> or an onclick= attribute in user-authored content turns into an XSS sink the moment your docs accept community contributions or render markdown from a CMS.
If you control every author and want raw HTML anyway, the safe pattern is the rehype-raw + rehype-sanitize pair:
import { docsShell } from '@publier/shell/integration';import rehypeRaw from 'rehype-raw';import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
export default defineConfig({ integrations: [docsShell()], markdown: { rehypePlugins: [ rehypeRaw, // sanitize-after-parse — never trust raw HTML before it's been allow-listed [rehypeSanitize, { ...defaultSchema, // allow only the tags + attrs you actually need tagNames: [...(defaultSchema.tagNames ?? []), 'iframe'], attributes: { ...defaultSchema.attributes, iframe: ['src', 'title', 'width', 'height', 'allow'], }, }], ], },});Order matters — rehype-raw first to lift HTML into the AST, rehype-sanitize after to strip anything outside the allow-list. Never run rehype-raw without a sanitizer if any of your content comes from untrusted authors.
For most docs sites, the right answer is “use a Publier component instead.” If you find yourself reaching for <iframe>, <details>, or a custom card layout, see Components — most of it is already there.
Pretty quotes / em-dashes (opt-in)
remark-smartypants converts "foo" → “foo” and -- → —. Not auto-wired (it’s stylistic). Add it via markdown.remarkPlugins — see Plugins.
Code fences
Fenced code goes through astro-expressive-code for syntax highlighting, copy buttons, and titles. Full reference at Code blocks. Quick examples:
```ts title="hello.ts"const greet = (name: string) => `Hello, ${name}!`;```const greet = (name: string) => `Hello, ${name}!`;What’s auto-wired vs. what isn’t
| Plugin | Auto-wired | Why |
|---|---|---|
remark-frontmatter | ✓ | Required for every page |
remark-gfm | ✓ | Universal expectation in docs |
remark-directive | ✓ | Powers :::tip callouts |
remark-asides (Publier) | ✓ | Renders directive nodes into <Aside> |
remark-math + rehype-katex | opt-in | Heavy peer deps; only enable when needed |
astro-mermaid | auto | Detected when a mermaid fence appears |
remark-smartypants | — | Stylistic; opinionated |
rehype-raw | — | Security risk by default; opt-in only |
rehype-sanitize | — | Pair with rehype-raw if you opt in |
rehype-format | — | Pretty-prints HTML; cosmetic only, no runtime benefit |
Anything not listed here is still pluggable — append to markdown.remarkPlugins / markdown.rehypePlugins in astro.config.ts. See Plugins for the integration pattern.
Related
- Authoring conventions → Content authoring
- Callouts deep-dive → Callouts
- Code blocks reference → Code blocks
- Custom plugin pipeline → Plugins
Footnotes
-
Footnotes work too —
remark-gfmlifts them to the bottom of the page. ↩