MDX and Markdown

MDX and Markdown content routes with JSX support, bundled @mdx-js/rollup - no config needed.

What is MDX?

MDX extends Markdown to allow JSX components directly in Markdown files. Bini.js supports .mdx and .md out of the box. @mdx-js/rollup is bundled internally - no separate install or Vite config required.

FeatureDescriptionCreates URL?
.mdx filesMarkdown + JSX componentsYes - content route creates URL
.md filesPlain markdown through MDX pipelineYes - content route creates URL
No config@mdx-js/rollup bundledNo - build setup

MDX Pages

Create an MDX page by adding .mdx anywhere in src/app/. The file compiles to a React component and creates a URL based on the folder/file name.

app
about.mdx
blog
page.mdx
[slug].mdx
contact.mdx
/about
/blog
/blog/:slug
/contact
app/about.mdx
export const metadata = {
  title: 'About Us',
  description: 'Learn more about our company',
}

# About Us

Welcome to our company! This is a regular **Markdown** page with JSX support.

<Button variant="primary">Get Started</Button>

## Our Mission

We build amazing products with Bini.js.

Markdown Pages

Plain .md files go through the same MDX pipeline - they also support JSX and imports. There is no plain-markdown-only mode. Creates a URL like MDX.

app
docs
getting-started.md
privacy.md
terms.md
/docs/getting-started
/privacy
/terms
app/terms.md
# Terms of Service

## 1. Acceptance of Terms

By using our service, you agree to these terms.

## 2. User Responsibilities

Users are responsible for their content and activity.

## 3. Termination

We reserve the right to terminate accounts that violate these terms.

---

*Last updated: January 2024*
Both .mdx and .md are compiled through the same MDX pipeline with full JSX, import, and export support. Each creates a URL.

Metadata in MDX

Export metadata from any MDX page to set titles, descriptions, and Open Graph tags. Works the same as page.tsx.

app/blog/post.mdx
export const metadata = {
  title: 'Blog Post',
  description: 'A comprehensive guide to Bini.js',
  openGraph: {
    title: 'Blog Post',
    description: 'A comprehensive guide to Bini.js',
    images: ['/og-image.png'],
  },
  twitter: {
    card: 'summary_large_image',
    title: 'Blog Post',
    creator: '@bini_js',
  },
}

# Blog Post

This is a blog post written in MDX with full metadata support.

Root layout metadata is injected into index.html at build time. Nested layout titles update document.title at runtime.

Imports in MDX

Import components, utilities, and hooks directly in MDX. Auto-imports like useState, Link, and getEnv apply to MDX the same as pages.

app/interactive.mdx
import { Button } from '@/components/Button'
import { BlogLayout } from '@/components/BlogLayout'
import { useTheme } from '@/hooks/useTheme'

export const metadata = {
  title: 'Interactive Page',
}

# Interactive Page

<BlogLayout>
  <p>This page uses imported components!</p>
  <Button variant="primary">Click Me</Button>
</BlogLayout>

Extension Priority

When multiple files share the same base name in a folder, priority order determines which creates the URL.

.tsx > .jsx > .ts > .js > .mdx > .md
app
about
page.tsx
page.mdx
blog
page.mdx
page.md
contact.md
/about
/blog
/contact
FolderUsed FileCreates URL?Ignored
app/aboutpage.tsxYes - higher prioritypage.mdx
app/blogpage.mdxYes - higher than .mdpage.md
app/contactcontact.mdYes - only file-

Styling MDX Content

CSS Modules, plain CSS imports, and Tailwind utility classes work directly in MDX files.

app/about.mdx
import styles from './About.module.css'
import { Button } from '@/components/Button'

# About Us

<div className={styles.container}>
  <p className="text-slate-600 dark:text-slate-300">
    This uses Tailwind classes and CSS Modules!
  </p>
  <Button>Learn More</Button>
</div>
Tailwind Preflight strips default heading/bold styling. Wrap plain markdown in a prose class from @tailwindcss/typography if you want default typography styles.

Complete Example

Comprehensive MDX/Markdown usage - each content file creates a URL.

app
layout.tsx
page.tsx
about.mdx
blog
layout.tsx
page.mdx
loading.tsx
[slug].mdx
_components
PostCard.tsx
docs
[[...slug]]
page.md
contact.mdx
/
/about
/blog
/blog/:slug
/docs/*
/contact
app/about.mdx
export const metadata = {
  title: 'About',
  description: 'Learn about our company',
}

import { TeamMember } from '@/components/TeamMember'

# About Our Company

We build amazing things with Bini.js.

<div className="grid grid-cols-2 gap-4">
  <TeamMember name="John" role="Developer" />
  <TeamMember name="Jane" role="Designer" />
</div>

## Our Values

- **Quality** - We ship polished code
- **Speed** - We move fast
- **Community** - We support our users
File PathURLCreates URL?
app/page.tsx/Yes
app/about.mdx/aboutYes - MDX creates URL
app/blog/page.mdx/blogYes - MDX creates URL
app/blog/[slug].mdx/blog/:slugYes - dynamic MDX creates URL
app/docs/[[...slug]]/page.md/docs/*Yes - MD catch-all creates URL
app/_components/Header.tsx-No - private
app/docs/_components/Sidebar.tsx-No - private
Was this helpful?