---
title: Syntax
description: Learn about the supported markdown syntax and formatting options
type: reference
summary: Supported MDX components and syntax including tabs, text formatting, code blocks, line highlighting, and Mermaid diagrams.
url: /docs/syntax
source: apps/template/content/docs/syntax.mdx
prerequisites:
  - /docs/getting-started
related:
  - /docs/configuration
---

# Syntax

Geistdocs supports MDX, a superset of Markdown that allows you to use JSX components within your documentation. This guide covers all the formatting options available.

  Help me write a Geistdocs MDX page. Use this page as a syntax reference and
  create an example page with frontmatter, headings, links, a code block, a
  `Callout`, and a `CopyPrompt` component.

## Frontmatter

Every MDX file should include frontmatter at the top:

```yaml
---
title: Page Title
description: A brief description of the page content
navTitle: Short page title
badge: Beta
---
```

The `title` property is required and is used for the page heading, metadata, and default navigation label. Set the optional `navTitle` when navigation needs a shorter label without changing the page heading. The `description` is used for SEO and page previews. The optional `badge` appears beside the page label in desktop and mobile sidebar navigation. Badge labels that match an uppercase HTTP method (`GET`, `POST`, `PUT`, `PATCH`, `DEL`, or `DELETE`) render with a method-specific color. See [Configure sidebar navigation](/docs/guides/nested-navigation) for the full color mapping.

## Basic Markdown

### Text Formatting

You can style text using standard Markdown syntax:

- **Bold text** with `**double asterisks**`
- _Italic text_ with `*single asterisks*`
- ~~Strikethrough~~ with `~~double tildes~~`
- `Inline code` with `` `backticks` ``

### Headings

Use `#` symbols to create headings:

```text
# Heading 1

## Heading 2

### Heading 3

#### Heading 4

##### Heading 5

###### Heading 6
```

Headings automatically generate anchor links for navigation and deep linking.

### Lists

Create unordered lists with `-`, `*`, or `+`:

```md
- Install the required packages
- Configure your project
- Start the development server
```

This renders as:

- Install the required packages
- Configure your project
- Start the development server

Create ordered lists with numbers:

1. First step
2. Second step
3. Third step

### Steps

Headings that start with a number, such as `### 1. Install the CLI`, render as a numbered step list with a counter beside each heading. See [Steps](/docs/components/steps) for the grouping rules and the `Steps` and `Step` components.

### Blockquotes

Use `>` to create blockquotes:

> **Watch:** Learn more about how to use `next/image` → [YouTube (9 minutes)](https://youtu.be/IU_qq_c_lKA).

### Callouts

Use `<Callout>` for a highlighted informational note:

```mdx
<Callout>This is automatically set when deploying to Vercel.</Callout>
```

This renders as:

> This is automatically set when deploying to Vercel.

Callouts can also contain a title and lists:

> Note:
  - The `App Router` uses [React canary
  releases](https://react.dev/blog/2023/05/03/react-canaries) built-in, which
  include all the stable React 19 changes, as well as newer features being
  validated in frameworks, but you should still declare react and react-dom in
  package.json for tooling and ecosystem compatibility.
  - The `Pages Router` uses the React version from your `package.json`.

Callouts support rich inline Markdown, including code, emphasis, and links:

```mdx
<Callout title="Good to know">
  Global styles can be imported into any layout, page, or component inside the
  `app` directory. However, since Next.js uses React's built-in support for
  stylesheets to integrate with Suspense, this currently does not remove
  stylesheets as you navigate between routes which can lead to conflicts. We
  recommend using global styles for _truly global_ CSS (like Tailwind's base
  styles), [Tailwind CSS](https://tailwindcss.com) for component styling, and
  [CSS Modules](https://nextjs.org/docs/app/getting-started/css#css-modules) for
  custom scoped CSS when needed.
</Callout>
```

This renders as:

> Note:
  Global styles can be imported into any layout, page, or component inside the
  `app` directory. However, since Next.js uses React's built-in support for
  stylesheets to integrate with Suspense, this currently does not remove
  stylesheets as you navigate between routes which can lead to conflicts. We
  recommend using global styles for _truly global_ CSS (like Tailwind's base
  styles), [Tailwind CSS](https://tailwindcss.com) for component styling, and
  [CSS Modules](https://nextjs.org/docs/app/getting-started/css#css-modules) for
  custom scoped CSS when needed.

### Disclosures

Use `<Details>` and `<Summary>` to hide supporting information until the reader
chooses to expand it:

```mdx
<Details>
  <Summary>Graph algorithm overview</Summary>

  The graph starts with the CSS files imported by each route.
  It groups files when sharing them costs less than another request.
  It splits files when unused CSS becomes more expensive than that request.
</Details>
```

This renders as:

<Details>
  <Summary>Graph algorithm overview</Summary>

  The graph starts with the CSS files imported by each route.
  It groups files when sharing them costs less than another request.
  It splits files when unused CSS becomes more expensive than that request.
</Details>

### Copy Prompts

Short prompts render in full without an expansion control:

```mdx
<CopyPrompt text="Summarize this documentation page and list the key concepts.">
  Summarize this documentation page and list the key concepts.
</CopyPrompt>
```

This renders as:

  Summarize this documentation page and list the key concepts.

Long prompts initially show a preview with a **Show more** control:

```mdx
<CopyPrompt collapsible text="Write a detailed migration plan for this project.">
  Write a detailed migration plan for this project. Include prerequisites,
  implementation steps, validation, rollback guidance, and common pitfalls.
</CopyPrompt>
```

Set the boolean `collapsible` prop when the prompt should initially render as a
two-line preview. This renders as:

  Review this Geistdocs project in detail. Explain how the `@vercel/geistdocs`
  package, application layouts, local adapter files, source configuration,
  `content/docs` directory, navigation metadata, MDX components, syntax
  highlighting, search routes, and deployment configuration work together.
  Then identify the safest first files to edit, describe the effect of each
  change, call out common mistakes, and propose a step-by-step plan for
  customizing the documentation site without breaking routing, generated
  Markdown, or accessibility behavior.

### Links

Create links with `[text](url)`:

- [External link](https://vercel.com)
- [Internal link](/docs/getting-started)

Use an absolute `https://` or `http://` URL for an external destination. Use a
root-relative path beginning with `/` for an internal link. Both use the same
visual styling:

```md
[External link](https://vercel.com)
[Internal link](/docs/getting-started)
```

Links can also contain inline code:

```md
Run independent requests in parallel with [`Promise.all`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise/all).
```

This renders as:

Run independent requests in parallel with [`Promise.all`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise/all).

#### Force browser-native navigation

Normal internal links use Next.js navigation, while external links disable
prefetching automatically. A custom `createDocsPage` link resolver also
receives `nativeLink` for URLs that must always use a regular `<a>`, such as a
raw Markdown file or a production URL that should not be localized into a
preview deployment. It keeps the same Geistdocs link styling:

```tsx
const docsPage = createDocsPage({
  config,
  source,
  resolveLink: ({ link: Link, nativeLink: NativeLink }) =>
    function ResolvedLink(props) {
      const native =
        props.href?.endsWith(".md") ||
        props.href?.startsWith("https://example.com");

      return native ? <NativeLink {...props} /> : <Link {...props} />;
    },
});
```

`NativeDocsLink` is also exported from the selected package's
`components/link` subpath for use outside `createDocsPage`.

### Images

Add images with `![alt text](url)`:

```md
![Description of the image](/path/to/image.png)
```

Use `<Image>` when an image needs the standard documentation presentation and
an optional description. It uses Geistcn's image component without a lightbox
or other interaction:

```mdx
<Image
  alt="Deployment graph"
  caption="Requests move from the edge to the selected region."
  height={480}
  src="/deployment-graph.png"
  width={800}
/>
```

This renders as:

<Image
  alt="Navigation Inspector showing a loading shell for a page load"
  caption="The Navigation Inspector paused on a page load."
  height={389}
  srcDark="https://h8dxkfmaphn8o0p3.public.blob.vercel-storage.com/docs/dark/inspector-load.png"
  srcLight="https://h8dxkfmaphn8o0p3.public.blob.vercel-storage.com/docs/light/inspector-load.png"
  unoptimized
  width={472}
/>

For theme-aware images, provide both sources:

```mdx
<Image
  alt="Deployment graph"
  caption="Requests move from the edge to the selected region."
  height={480}
  srcDark="/deployment-graph-dark.png"
  srcLight="/deployment-graph-light.png"
  width={800}
/>
```

### Tabs

Use `TabsWithChildren` and `TabContent` to switch between related instructions,
code examples, or other MDX components. Both are available without imports.
The `tabs` array defines the labels, and each panel's `order` starts at `1`.

```mdx
<TabsWithChildren tabs={["Dashboard", "CLI"]} ariaLabel="Setup method">
  <TabContent order={1}>
    Open your project's settings in the dashboard.
  </TabContent>
  <TabContent order={2}>
    Connect your local project using the CLI.
  </TabContent>
</TabsWithChildren>
```

This renders as:

<TabsWithChildren tabs={["Dashboard", "CLI"]} ariaLabel="Setup method">
  <TabContent order={1}>
    Open your project's settings in the dashboard.
  </TabContent>
  <TabContent order={2}>
    Connect your local project using the CLI.
  </TabContent>
</TabsWithChildren>

Use the arrow keys, Home, or End to select a tab. Each tab is linked to its panel
for assistive technology. Inactive panels stay mounted and hidden, preserving
local state when you return to a tab. Nested tab groups manage their selection
independently.

In React, use `defaultValue="tab2"` to start on the second tab, or pass `value`
and `onValueChange` to control selection. Values follow the panel order
(`tab1`, `tab2`, and so on).

## GitHub Flavored Markdown

Geistdocs supports GFM extensions including tables, task lists, and autolinks.

### Tables

Create tables using pipes and dashes:

```md
| Feature  | Description       | Status    |
| -------- | ----------------- | --------- |
| Markdown | Basic formatting  | Supported |
| GFM      | GitHub extensions | Supported |
| MDX      | JSX in Markdown   | Supported |
```

This renders as:

| Feature  | Description       | Status    |
| -------- | ----------------- | --------- |
| Markdown | Basic formatting  | Supported |
| GFM      | GitHub extensions | Supported |
| MDX      | JSX in Markdown   | Supported |

Tables can also include inline code, as commonly used for file references:

| File                 | Description                                      |
| -------------------- | ------------------------------------------------ |
| `next.config.js`     | Configuration file for Next.js                   |
| `package.json`       | Project dependencies and scripts                 |
| `instrumentation.ts` | OpenTelemetry and instrumentation file           |
| `.env.local`         | Local environment variables                      |
| `tsconfig.json`      | Configuration file for TypeScript                |

### Task Lists

Create interactive task lists:

```md
- [x] Completed task
- [ ] Incomplete task
- [ ] Another task to do
```

This renders as:

- [x] Completed task
- [ ] Incomplete task
- [ ] Another task to do

### Autolinks

URLs are automatically converted to clickable links:

```md
https://vercel.com
```

This renders as:

https://vercel.com

## Code Blocks

Fenced code blocks support syntax highlighting for many languages.

### Basic Syntax Highlighting

Specify the language after the opening backticks:

````md
```typescript
const greet = (name: string): string => {
  return `Hello, ${name}!`;
};
```
````

This renders as:

```typescript
const greet = (name: string): string => {
  return `Hello, ${name}!`;
};
```

### Titles

Add a title to your code block with the `title` attribute:

````md
```tsx title="components/button.tsx"
export const Button = ({ children }) => {
  return <button type="button">{children}</button>;
};
```
````

This renders as:

```tsx title="components/button.tsx"
export const Button = ({ children }) => {
  return <button type="button">{children}</button>;
};
```

### Tabs and switchers

Code blocks support tabs and compact switchers for selecting between code examples.
Use consecutive fences with `tab` metadata to show all code tab labels at once:

````md
```ts tab="TypeScript" title="example.ts"
export const greeting: string = "Hello from TypeScript";
```

```js tab="JavaScript" title="example.js"
export const greeting = "Hello from JavaScript";
```
````

This renders as:

```ts tab="TypeScript" title="example.ts"
export const greeting: string = "Hello from TypeScript";
```

```js tab="JavaScript" title="example.js"
export const greeting = "Hello from JavaScript";
```

Wrap the same fences in `<CodeBlockTabs compact>` to move the selector into the
code-block header:

````mdx
<CompactCodeBlockTabs defaultValue="TypeScript">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="TypeScript">TypeScript</CodeBlockTabsTrigger>
    <CodeBlockTabsTrigger value="JavaScript">JavaScript</CodeBlockTabsTrigger>
  </CodeBlockTabsList>
  <CodeBlockTab value="TypeScript">
    ```ts title="example.ts"
    export const greeting: string = "Hello from TypeScript";
    ```
  </CodeBlockTab>
  <CodeBlockTab value="JavaScript">
    ```js title="example.js"
    export const greeting = "Hello from JavaScript";
    ```
  </CodeBlockTab>
</CompactCodeBlockTabs>
````

This renders as:

<CompactCodeBlockTabs defaultValue="TypeScript">
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="TypeScript">TypeScript</CodeBlockTabsTrigger>
    <CodeBlockTabsTrigger value="JavaScript">JavaScript</CodeBlockTabsTrigger>
  </CodeBlockTabsList>
  <CodeBlockTab value="TypeScript">
    ```ts title="example.ts"
    export const greeting: string = "Hello from TypeScript";
    ```
  </CodeBlockTab>
  <CodeBlockTab value="JavaScript">
    ```js title="example.js"
    export const greeting = "Hello from JavaScript";
    ```
  </CodeBlockTab>
</CompactCodeBlockTabs>

When migrating an existing site such as next-site, translate adjacent
`switcher` fences to `tab="TypeScript"` and `tab="JavaScript"`, then wrap the
pair with `<CodeBlockTabs compact>`. Import `@vercel/geistdocs/styles.css` (or
`theme.css` with the documented Geist dependencies) so Tailwind scans the
package's compiled components and emits the tab layout utilities.

### Framework icons

Append `#<framework>` to the title to show a framework logo instead of the
language icon. The suffix is stripped from the displayed title. Supported
values are `#next` / `#nextjs` and `#svelte` / `#sveltekit`:

````md
```ts title="flags.ts#next"
import { flag } from "flags/next";
```

```ts title="flags.ts#svelte"
import { flag } from "flags/sveltekit";
```
````

This renders as:

```ts title="flags.ts#next"
import { flag } from "flags/next";
```

```ts title="flags.ts#svelte"
import { flag } from "flags/sveltekit";
```

### Line Numbers

Display line numbers by adding the `lineNumbers` attribute:

````md
```typescript lineNumbers
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((n) => n * 2);
console.log(doubled);
```
````

This renders as:

```typescript lineNumbers
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map((n) => n * 2);
console.log(doubled);
```

### Line Highlighting

Highlight specific lines using the `[!code highlight]` comment:

````md
```typescript
const config = {
  theme: "dark", // [ !code highlight]
  language: "en",
};
```
````

Remove the space before `!` when using this syntax. This renders as:

```typescript
const config = {
  theme: "dark", // [!code highlight]
  language: "en",
};
```

### Word Highlighting

Highlight specific words with `[!code word:term]`:

````md
```typescript
// [ !code word:config]
const config = {
  theme: "dark",
};

console.log(config);
```
````

Remove the space before `!` when using this syntax. This renders as:

```typescript
// [!code word:config]
const config = {
  theme: "dark",
};

console.log(config);
```

### Diff Syntax

Show additions and deletions with `[!code ++]` and `[!code --]`:

````md
```typescript
const config = {
  theme: "light", // [ !code --]
  theme: "dark", // [ !code ++]
  language: "en",
};
```
````

Remove the space before `!` when using this syntax. This renders as:

```typescript
const config = {
  theme: "light", // [!code --]
  theme: "dark", // [!code ++]
  language: "en",
};
```

### Focus Lines

Draw attention to specific lines with `[!code focus]`:

````md
```typescript
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((a, b) => a + b, 0); // [ !code focus]
console.log(sum);
```
````

Remove the space before `!` when using this syntax. This renders as:

```typescript
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((a, b) => a + b, 0); // [!code focus]
console.log(sum);
```

## Mermaid Diagrams

Create diagrams and flowcharts using Mermaid syntax.

### Flowcharts

````md
```mermaid
graph TD;
    A[Start] --> B{Is it working?};
    B -->|Yes| C[Great!];
    B -->|No| D[Debug];
    D --> B;
```
````

This renders as:

```mermaid
graph TD;
    A[Start] --> B{Is it working?};
    B -->|Yes| C[Great!];
    B -->|No| D[Debug];
    D --> B;
```

### Sequence Diagrams

````md
```mermaid
sequenceDiagram
    participant User
    participant API
    participant Database
    User->>API: Request data
    API->>Database: Query
    Database-->>API: Results
    API-->>User: Response
```
````

This renders as:

```mermaid
sequenceDiagram
    participant User
    participant API
    participant Database
    User->>API: Request data
    API->>Database: Query
    Database-->>API: Results
    API-->>User: Response
```

### Architecture Diagrams

````md
```mermaid
graph TD;
    subgraph Frontend
        A[Web App]
        B[Mobile App]
    end
    subgraph Backend
        C[API Gateway]
        D[Auth Service]
        E[Data Service]
    end
    subgraph Storage
        F[(Database)]
        G[(Cache)]
    end
    A --> C
    B --> C
    C --> D
    C --> E
    E --> F
    E --> G
```
````

This renders as:

```mermaid
graph TD;
    subgraph Frontend
        A[Web App]
        B[Mobile App]
    end
    subgraph Backend
        C[API Gateway]
        D[Auth Service]
        E[Data Service]
    end
    subgraph Storage
        F[(Database)]
        G[(Cache)]
    end
    A --> C
    B --> C
    C --> D
    C --> E
    E --> F
    E --> G
```
