@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.js
  • rehype-slug — adds id attributes to headings for anchor links
  • rehype-autolink-headings — adds anchor links to headings
  • remark-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:

  1. Rename .page.md.page.mdx
  2. The frontmatter and content work identically — no changes needed for pure Markdown content
  3. 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:

PropTypeDescription
activeClassstringCSS class applied when href matches the current pathname
externalbooleanAdds 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.