@davaux/mdx
Adds support for .page.mdx route files. MDX is Markdown that can import and use JSX components inline, making it ideal for documentation sites, blogs, and any content-heavy page that needs occasional interactive or styled elements embedded in prose.
Installation
npm install @davaux/mdx
Setup
Register the plugin in davaux.config.ts:
// davaux.config.ts
import { defineConfig } from 'davaux/config'
import { mdx } from '@davaux/mdx'
export default defineConfig({
plugins: [mdx()],
})
Using remark and rehype plugins
MDX processes your files through a remark (Markdown AST) → rehype (HTML AST) pipeline. Pass plugins to either stage:
import { mdx } from '@davaux/mdx'
import rehypeHighlight from 'rehype-highlight'
import remarkFrontmatter from 'remark-frontmatter'
export default defineConfig({
plugins: [
mdx({
remarkPlugins: [remarkFrontmatter],
rehypePlugins: [rehypeHighlight],
}),
],
})
remark-gfm is included by default — tables, strikethrough, task lists, and autolinks work out of the box without any configuration.
Common plugins to add:
rehype-highlight— syntax highlighting via highlight.jsrehype-slug— addsidattributes to headings for anchor linksrehype-autolink-headings— adds anchor links to headingsremark-frontmatter— additional frontmatter format support
Creating MDX pages
Any file matching *.page.mdx inside src/routes/ becomes a route:
src/routes/
├── docs/
│ ├── getting-started.page.mdx → GET /docs/getting-started
│ └── api-reference.page.mdx → GET /docs/api-reference
└── changelog.page.mdx → GET /changelog
Frontmatter
YAML frontmatter is parsed and automatically applied to ctx.head:
---
title: Getting Started
description: Learn how to build your first app with Davaux.
---
# Getting Started
Welcome! Let's build something.
The title becomes ctx.head.title and description becomes ctx.head.description — read by your root layout to populate <title> and <meta name="description">.
Embedding JSX components
Import any JSX component and use it inline in your Markdown:
---
title: Component Demo
---
import { Callout } from '../../components/Callout.tsx'
import Counter from '../../islands/Counter.tsx'
# Component Demo
Here is a callout component embedded in prose:
<Callout type="info">
This is a **callout** with Markdown inside.
</Callout>
And here is a live interactive counter island:
<Counter initial={10} />
Back to regular Markdown below.
The Counter island is fully hydrated in the browser — all the island behavior described in the Islands page applies here.
JSX import source
MDX compiles JSX using the jsxImportSource from your tsconfig.json. Make sure it points to 'davaux':
{
"compilerOptions": {
"jsxImportSource": "davaux"
}
}
This ensures MDX-generated JSX uses the correct runtime for server rendering.
Syntax highlighting
Pair rehype-highlight with the highlight.js theme of your choice for code block syntax highlighting:
mdx({ rehypePlugins: [rehypeHighlight] })
In your layout, link a highlight.js theme CSS file:
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.10.0/styles/github-dark.min.css"
/>
Language is auto-detected from the fenced code block language identifier:
```ts
const greeting: string = 'Hello, MDX!'
```
Migrating from .page.md
Both @davaux/markdown and @davaux/mdx can coexist. To migrate an individual page:
- Rename
.page.md→.page.mdx - The frontmatter and content work identically — no changes needed for pure Markdown content
- You can now add JSX imports and component usage anywhere in the file
The plugins handle their respective extensions independently, so you can migrate incrementally.
Links and base path
When your app uses basePath (see Configuration), MDX pages automatically have access to a base-path-aware Link component — no import needed.
<Link> component
Use <Link> instead of <a> for internal links. The href is automatically prefixed with basePath at render time:
<Link href="/getting-started" class="btn">Get started</Link>
Link accepts all standard anchor props plus:
| Prop | Type | Description |
|---|---|---|
activeClass | string | CSS class applied when href matches the current pathname |
external | boolean | Adds target="_blank" rel="noopener noreferrer" |
<Link href="/getting-started" activeClass="current">Getting Started</Link>
<Link href="https://codeberg.org/davaux/davaux" external>View source</Link>
Markdown links
Standard Markdown link syntax [text](/path) also gets basePath applied automatically — no changes needed in your content:
See the [Getting Started](/getting-started) guide.
Both link forms are handled by the same underlying createLink utility described in Request Context.
Exporting from MDX pages
MDX files can export named values just like TypeScript modules:
---
title: My Post
---
export const metadata = {
author: 'Alice',
publishedAt: '2026-05-15',
tags: ['davaux', 'mdx'],
}
# My Post
Content here.
Import these exports in server-side listing pages to build dynamic indexes without a CMS.