Skip to content
K

Markdown Features

Write Markdown and MDX that can stay simple until a page needs real React.

Ardo is Markdown-first, but it does not trap you in Markdown. Start with plain prose and code blocks; when a page needs a live React example, a custom component, or a design-system callout, switch to MDX without changing tools.

Basic Syntax

Headings

# Heading 1

## Heading 2

### Heading 3

#### Heading 4

Emphasis

_italic_ or _italic_
**bold** or **bold**
**_bold and italic_**
~~strikethrough~~

Lists

- Unordered item 1
- Unordered item 2
  - Nested item

1. Ordered item 1
2. Ordered item 2
[Link text](https://example.com)
![Alt text](/path/to/image.png)

GitHub Flavored Markdown

Ardo supports GitHub Flavored Markdown (GFM) out of the box.

Tables

FeatureSupported
TablesYes
Task ListsYes
AutolinksYes

Task Lists

  • Write documentation
  • Add syntax highlighting
  • Deploy to production

URLs are automatically converted to links: https://reactrouter.com

Syntax Highlighting

Code blocks are automatically highlighted using Shiki. The following languages are bundled and work out of the box:

CategoryLanguages
Webjavascript, typescript, jsx, tsx, html, css, scss
Data & configjson, jsonc, yaml, toml, xml, graphql
Markdownmarkdown, mdx
Shell & DevOpsbash, shell, dockerfile
General purposepython, rust, go, sql, diff
interface User {
  id: string
  name: string
  email: string
}

function greetUser(user: User): string {
  return `Hello, ${user.name}!`
}

Line Highlighting

Highlight specific lines using the {lines} syntax:

function example() {
  const highlighted = true
  const normal = false
  const alsoHighlighted = true
  const highlighted = true
  const notHighlighted = false
}

Line Numbers

Enable line numbers with showLineNumbers:

const first = 1
const second = 2
const third = 3

Title

Add a title to your code block:

utils/greeting.ts
export function greet(name: string) {
  return `Hello, ${name}!`
}

Badge Component

Use badges for inline status labels, version indicators, and metadata tags. The Badge component is auto-registered in MDX files — no import needed.

This feature is New, available for v2.0+, and can mark Beta or Deprecated content.

<Badge>New</Badge>
<Badge variant="success">Stable</Badge>
<Badge variant="warning">Beta</Badge>
<Badge variant="danger">Deprecated</Badge>
<Badge variant="info">v2.0+</Badge>

Badges also support an optional leading icon:

<Badge icon="✨">New</Badge>

Callout Components

Use callouts to highlight important information. Ardo accepts two equivalent syntaxes — pick whichever reads better in your file.

GitHub-style alerts

Plain Markdown, no imports. Ardo recognises GFM alert syntax and maps each type to the matching callout.

> [!NOTE]
> Useful information that users should know.

> [!TIP]
> Helpful advice for doing things better.

> [!IMPORTANT]
> Key information users need to know.

> [!WARNING]
> Urgent info that needs immediate attention.

> [!CAUTION]
> Negative potential consequences of an action.

Renders as:

Useful information that users should know.

Helpful advice for doing things better.

Key information users need to know.

Urgent info that needs immediate attention.

Negative potential consequences of an action.

JSX components

When you need a custom title or full control, the same callouts are also available as JSX. These components are auto-registered in MDX files — no import needed.

This is a helpful tip for readers.
Be careful when using this feature.
This action cannot be undone!
Here's some additional information.
This is a note for reference.

Custom titles

You can customize the callout title:

Pro Tip

This is a pro tip with a custom title.

Accordions

Use Accordion and AccordionGroup for collapsible FAQ items, optional explanations, and compact reference sections. Add onlyOneOpen to the group when only one item should stay open at a time.

<AccordionGroup onlyOneOpen>
  <Accordion title="How do I install Ardo?" defaultOpen>
    Run `pnpm create ardo` to scaffold a new project.
  </Accordion>
  <Accordion title="Can I use it with existing projects?">
    Yes, Ardo can be added to an existing React Router project.
  </Accordion>
</AccordionGroup>

Run pnpm create ardo to scaffold a new project.

Each accordion also supports an optional icon and can be used standalone without a group.

Code Groups

Display multiple code variants in tabs:

function greet(name) {
  return `Hello, ${name}!`
}

ArdoCodeBlock in .tsx Routes

The ArdoCodeBlock component can be used directly in .tsx route files. Just write the code as children — no template literals or special escaping needed:

import { ArdoCodeBlock } from "ardo/ui"

export default function MyPage() {
  return (
    <ArdoCodeBlock language="tsx">
      export function Hello() {
        return <div>Hello world!</div>
      }
    </ArdoCodeBlock>
  )
}

The ardo:codeblock-highlight Vite plugin processes ArdoCodeBlock before the JSX parser runs, so the children can contain any syntax (<, {, >, etc.) without causing errors. Common indentation is stripped automatically.

The code prop and template literal children are also supported:

<ArdoCodeBlock code="const x = 42" language="typescript" />

If the language prop is dynamic (from a variable), build-time highlighting is skipped and the code renders as plain text.

Reusable MDX Snippets

Ardo uses standard MDX modules, so reusable content snippets work without a custom syntax or plugin. Put shared fragments in a snippets/ directory and import them wherever you need the same content.

For example, define a shared prerequisite note in app/snippets/prerequisites.mdx. Snippet files do not need frontmatter when they are rendered inside another page:

<Tip title="Before you start">
  This guide assumes you have {props.runtime} installed and can run packages with{" "}
  {props.packageManager}.
</Tip>

Then import and render it from any MDX page inside ArdoBareContent so the snippet does not bring its own page wrapper:

import { ArdoBareContent } from "ardo/ui"
import Prerequisites from "../../snippets/prerequisites.mdx"

# Getting Started

<ArdoBareContent>
  <Prerequisites runtime="Node.js 22+" packageManager="pnpm" />
</ArdoBareContent>

Snippets can receive props in normal MDX expressions, compose Ardo components, and live alongside the rest of your application code. Because they are just MDX modules, Vite handles hot reloads and imports the same way as other route dependencies.

Use a relative import that matches your route depth. For example, a page in app/routes/guide/ usually imports ../../snippets/prerequisites.mdx.

Embedding MDX Snippets with ArdoBareContent

When you import an .mdx file into a .tsx route, it normally renders inside the full Content wrapper (article, header, footer, navigation). This is useful for full pages, but snippets usually need to render without that page chrome. To embed an imported MDX file as a plain content snippet in TSX, wrap it in ArdoBareContent:

import { ArdoBareContent } from "ardo/ui"
import MySnippet from "./snippet.mdx"

export default function MyPage() {
  return (
    <ArdoBareContent>
      <MySnippet />
    </ArdoBareContent>
  )
}

Custom Components

You can use React components directly in your markdown:

import { MyComponent } from './components/MyComponent'

# My Page

<MyComponent prop="value" />

Frontmatter

Add metadata to your pages using YAML frontmatter:

---
title: Page Title
description: Page description for SEO
layout: doc
sidebar: true
outline: [2, 3]
---

See the Frontmatter Reference for all available options.