MDX Support
Author Markdown-driven pages that compile to Xote views and can embed Xote components.
MDX support lets Markdown documents compile to Xote views. Use it when you want docs, articles, or rich long-form pages to live as .mdx files while still embedding Xote components.
Status: The MDX runtime is intentionally small. It provides the JSX runtime bridge and component override helpers; your bundler still owns MDX compilation.
Setup
Install the MDX Plugin
Use the MDX Rollup plugin in the Vite app that imports .mdx files.
1npm install --save-dev @mdx-js/rollup
For GitHub Flavored Markdown features such as pipe tables, install remark-gfm too.
1npm install --save-dev remark-gfm
Configure Vite
Point MDX at Xote's automatic JSX runtime.
1// vite.config.js2import mdx from "@mdx-js/rollup"3import remarkGfm from "remark-gfm"4import { defineConfig } from "vite"56export default defineConfig({7 plugins: [8 mdx({9 jsxImportSource: "xote",10 jsxRuntime: "automatic",11 remarkPlugins: [remarkGfm],12 }),13 ],14})
The runtime entry points are exported from xote/jsx-runtime and xote/jsx-dev-runtime, which is what MDX uses when jsxImportSource is set to "xote".
Import MDX from ReScript
Bind the compiled MDX module as an Mdx.document, then render it into a View.node.
1// ArticlePage.res2@module("./article.mdx") external article: Mdx.document = "default"34let app = () => {5 <main class="article-shell">6 {Mdx.render(article, ())}7 </main>8}910View.mountById(app(), "root")
Component Overrides
Mapping Elements to Components
MDX can receive a components dictionary. Keys match Markdown/HTML element names or custom MDX component names; values are Xote components wrapped with Mdx.component.
1// ArticlePage.res2type mdxProps = {3 children?: Mdx.children,4 href?: string,5}67module Link = {8 let make = (props: mdxProps) => {9 <a href={props.href->Option.getOr("#")} target="_blank" rel="noreferrer">10 {View.fragment(11 switch props.children {12 | Some(children) => Mdx.childrenToNodes(children)13 | None => []14 },15 )}16 </a>17 }18}1920let components = Mdx.components([21 ("a", Mdx.component(Link.make)),22])2324let page = Mdx.render(article, ~components, ())
Rendering Children
MDX children arrive as JavaScript values because the MDX compiler owns the call shape. Use the helpers in Mdx to turn them back into Xote-friendly values.
Mdx.childrenToNodes(children)returns an array ofView.node.Mdx.childrenToText(children)extracts plain text for cases like syntax highlighting or heading IDs.
1// ArticleHeading.res2module Heading = {3 let make = props => {4 <h2 class="article-heading">5 {View.fragment(6 switch props.children {7 | Some(children) => Mdx.childrenToNodes(children)8 | None => []9 },10 )}11 </h2>12 }13}
Authoring MDX
Markdown and Xote Components
An MDX file can mix Markdown, HTML-like tags, and named components supplied through the components dictionary.
1{/* article.mdx */}23# MDX with Xote45This paragraph renders as Xote nodes.67<Example />89<a href="https://mdxjs.com">MDX link rendered through a Xote component override</a>
Markdown output can be overridden by key:
1// ArticlePage.res2let components = Mdx.components([3 ("h1", Mdx.component(ArticleTitle.make)),4 ("p", Mdx.component(LeadParagraph.make)),5 ("Example", Mdx.component(Example.make)),6])
GitHub Flavored Markdown
Enable remark-gfm when your content uses GFM syntax such as tables.
1{/* article.mdx */}23| Feature | Supported |4| --- | --- |5| Component overrides | Yes |6| Markdown tables | Yes, with `remark-gfm` |
Without remark-gfm, Markdown pipe tables are parsed as paragraph text by the base MDX parser.
Runtime Behavior
What the JSX Runtime Handles
Xote's MDX runtime turns MDX's automatic JSX calls into View.node values. It supports:
- HTML and SVG tags
- event props such as
onClick - static, signal, computed, and
Prop-wrapped attributes - fragments and nested children
- reserved
keyhandling for keyed reconciliation - MDX internal props such as
mdxTypeandoriginalType
What Stays in the Bundler
MDX parsing, remark plugins, rehype plugins, syntax extensions, and file loading are bundler concerns. Configure those in Vite or whatever build pipeline imports .mdx files.
Working Style
Best Practices
- Keep MDX content focused on prose and examples; move stateful UI into named Xote components.
- Use component overrides for links, code blocks, headings, and demo panels so MDX pages match the rest of the app.
- Type only the props each override needs, then wrap the component with
Mdx.component. - Add
remark-gfmwhen docs use tables, task lists, autolinks, or strikethrough.
Next Steps
- Read View for the component model used by MDX overrides.
- Read Server-Side Rendering if MDX pages are prerendered or hydrated.