# Reusable snippets (/create/reusable-snippets)

<!-- agent-signals: reading_time_min: 9 · est_tokens: 3672 · updated: 2026-09-23 -->
Related: [Changelogs](/create/changelogs.md), [Personalized content](/create/personalization.md), [Redirects](/create/redirects.md), [Custom domain](/customize/custom-domain.md)

One of the core principles of software development is DRY (Don't Repeat Yourself), which applies to documentation too. If you find yourself repeating the same content in multiple places, create a custom snippet for that content. Snippets contain content that you can import into other files to reuse. You control where the snippet appears on a page. If you ever need to update the content, you only need to edit the snippet rather than every file where the snippet appears.

<Note>
  Snippets are not currently supported in the web editor. To use snippets, edit your MDX files locally with the CLI or push snippet imports directly to your repository.
</Note>

## How snippets work [#how-snippets-work]

Snippets are any `.mdx`, `.md`, `.js`, or `.jsx` files imported into another file. You can place snippet files anywhere in your project.

When you import a snippet into another file, the snippet only appears where you import it and does not render as a standalone page. Any file in the `/snippets/` folder is always a snippet even if it is not imported into another file.

## Create snippets [#create-snippets]

Create a file with the content you want to reuse. Snippets can contain all content types supported by Mintlify and they can import other snippets. See [Nested snippets](#nested-snippets) for where to declare imports when nesting.

## Import snippets into pages [#import-snippets-into-pages]

Import snippets into pages using either an absolute or relative path.

* **Absolute imports**: Start with `/` for imports from the root of your project.
* **Relative imports**: Use `./` or `../` to import snippets relative to the current file's location.

The name you use to render an imported snippet as a JSX tag must start with an uppercase letter, such as `MySnippet`. MDX treats lowercase tags such as `<mySnippet />` as literal HTML or custom element names rather than references to imported snippets.

<Tip>
  Relative imports enable IDE navigation. Press <kbd>Cmd</kbd> and click a snippet name in your editor to jump directly to the snippet definition.
</Tip>

### Import text [#import-text]

<Steps>
  <Step title="Add content to your snippet file">
    Add the content you want to reuse.

    ```mdx wrap title="shared/my-snippet.mdx"
    Hello world! This is my content I want to reuse across pages.
    ```
  </Step>

  <Step title="Import the snippet into your destination file">
    Use either an absolute or relative path.

    <CodeGroup>
      <CodeBlockTabs defaultValue="Absolute import" groupId="absolute-import+relative-import">
        <CodeBlockTabsList>
          <CodeBlockTabsTrigger value="Absolute import">
            Absolute import
          </CodeBlockTabsTrigger>

          <CodeBlockTabsTrigger value="Relative import">
            Relative import
          </CodeBlockTabsTrigger>
        </CodeBlockTabsList>

        <CodeBlockTab value="Absolute import">
          ```mdx  
          ---
          title: "An example page"
          description: "This is an example page that imports a snippet."
          ---

          import MySnippet from "/shared/my-snippet.mdx";

          The snippet content displays beneath this sentence.

          <MySnippet />
          ```
        </CodeBlockTab>

        <CodeBlockTab value="Relative import">
          ```mdx  
          ---
          title: "An example page"
          description: "This is an example page that imports a snippet."
          ---

          import MySnippet from "../shared/my-snippet.mdx";

          The snippet content displays beneath this sentence.

          <MySnippet />
          ```
        </CodeBlockTab>
      </CodeBlockTabs>
    </CodeGroup>
  </Step>
</Steps>

### Nested snippets [#nested-snippets]

Snippets can import other snippets. Declare the import in the snippet file that uses the nested snippet, not in the page that imports the parent snippet.

Each file resolves its own imports. Imports declared on a page do not apply to the snippets that the page imports. A nested snippet that relies on a page-level import may render as empty content.

<Steps>
  <Step title="Import the nested snippet in the parent snippet file">
    Declare the import where you want to use the nested snippet.

    ```mdx title="shared/parent-snippet.mdx"
    import ChildSnippet from "/shared/child-snippet.mdx";

    This snippet renders another snippet beneath this sentence.

    <ChildSnippet />
    ```
  </Step>

  <Step title="Import only the parent snippet in your destination file">
    You do not need to import the nested snippet.

    ```mdx title="destination-file.mdx"
    ---
    title: "An example page"
    description: "This is an example page that imports a snippet containing a nested snippet."
    ---

    import ParentSnippet from "/shared/parent-snippet.mdx";

    <ParentSnippet />
    ```
  </Step>
</Steps>

### Import variables [#import-variables]

Reference variables from a snippet in a page.

<Steps>
  <Step title="Export variables from a snippet file">
    ```mdx title="shared/custom-variables.mdx"
    export const myName = "Ronan";

    export const myObject = { fruit: "strawberries" };

    ;
    ```
  </Step>

  <Step title="Import the snippet from your destination file and use the variable">
    ```mdx title="destination-file.mdx"
    ---
    title: "An example page"
    description: "This is an example page that imports a snippet with variables."
    ---

    import { myName, myObject } from "/shared/custom-variables.mdx";

    Hello, my name is {myName} and I like {myObject.fruit}.
    ```
  </Step>
</Steps>

<Note>
  Browsers evaluate MDX expressions, such as imported variables like `{myName}` and inline expressions like `{1 + 1}`. Their values do not appear in a page's initial HTML or in [offline exports](/deploy/export), so crawlers, LLMs, and other tools that do not run JavaScript see the surrounding text without them. Write values as plain text if they must be visible in those situations.
</Note>

### Import snippets with variables [#import-snippets-with-variables]

Use variables to pass data to a snippet when you import it.

<Steps>
  <Step title="Add variables to your snippet">
    Pass in properties when you import it. In this example, the variable is `{word}`.

    ```mdx title="shared/my-snippet.mdx"
    My keyword of the day is {word}.
    ```
  </Step>

  <Step title="Import the snippet into your destination file with the variable">
    The passed property replaces the variable in the snippet definition.

    ```mdx title="destination-file.mdx"
    ---
    title: "An example page"
    description: "This is an example page that imports a snippet with a variable."
    ---

    import MySnippet from "/shared/my-snippet.mdx";

    <MySnippet word="bananas" />
    ```
  </Step>
</Steps>

Variables also interpolate inside fenced code blocks. This is useful for snippets that include installation commands or other code examples that differ by package name, version, or environment.

````mdx title="shared/install-snippet.mdx"
export const InstallSnippet = ({ packageName }) => <></>;

Install the package:

```bash
npm install {packageName}
```
````

```mdx title="destination-file.mdx"
import InstallSnippet from "/shared/install-snippet.mdx";

<InstallSnippet packageName="@myorg/sdk" />
```

### Import React components [#import-react-components]

<Steps>
  <Step title="Create a snippet with a JSX component">
    See [React components](/customize/react-components) for more information.

    ```js title="components/my-jsx-snippet.jsx"
    export const MyJSXSnippet = () => {
      return (
        <div>
          <h1>Hello, world!</h1>
        </div>
      );
    };
    ```

    <Note>
      When creating JSX snippets, use arrow function syntax (`=>`) rather than function declarations. The `function` keyword is not supported in snippets.
    </Note>
  </Step>

  <Step title="Import the snippet">
    ```mdx title="destination-file.mdx"
    ---
    title: "An example page"
    description: "This is an example page that imports a snippet with a React component."
    ---

    import { MyJSXSnippet } from "/components/my-jsx-snippet.jsx";

    <MyJSXSnippet />
    ```
  </Step>
</Steps>

## Render content from structured data [#render-content-from-structured-data]

Keep data such as a list of SDK components, a support matrix, or a set of plans in one snippet and render it on multiple pages. When you modify the data, every table, list, or card built from it updates.

Store the data as a plain JSON object in a `.js` snippet with a named export. Then write a `.jsx` snippet that turns the data into markup.

<Note>
  Snippets must be `.mdx`, `.md`, `.js`, or `.jsx` files. You cannot import a `.json` or `.yaml` file directly. Keep the data in a `.js` snippet, or [generate one from your JSON or YAML source](#generate-snippets-and-pages-from-json-or-yaml).
</Note>

<Steps>
  <Step title="Export the data from a snippet">
    ```js title="snippets/sdk-components.js"
    export const sdkComponents = [
      { "name": "CardForm", "version": "2.4.0", "status": "Stable", "docs": "/components/card-form" },
      { "name": "PinReveal", "version": "1.9.2", "status": "Beta", "docs": "/components/pin-reveal" }
    ];
    ```
  </Step>

  <Step title="Create a snippet that renders the data">
    Loop over the data with `map()` and return HTML elements or Mintlify components.

    ```jsx title="snippets/components-table.jsx"
    export const ComponentsTable = ({ rows }) => (
      <table>
        <thead>
          <tr>
            <th>Component</th>
            <th>Version</th>
            <th>Status</th>
          </tr>
        </thead>
        <tbody>
          {rows.map((row) => (
            <tr key={row.name}>
              <td><a href={row.docs}>{row.name}</a></td>
              <td><code>{row.version}</code></td>
              <td>{row.status}</td>
            </tr>
          ))}
        </tbody>
      </table>
    );
    ```
  </Step>

  <Step title="Import both snippets and pass the data as a property">
    Filter or sort the data in the page to show a subset without duplicating it.

    ```mdx title="destination-file.mdx"
    ---
    title: "SDK components"
    description: "Every component in the SDK, with its current version and status."
    ---

    import { sdkComponents } from "/snippets/sdk-components.js";
    import { ComponentsTable } from "/snippets/components-table.jsx";

    The SDK includes {sdkComponents.length} components.

    <ComponentsTable rows={sdkComponents} />

    ## Stable components

    <ComponentsTable rows={sdkComponents.filter((row) => row.status === "Stable")} />
    ```
  </Step>
</Steps>

### Generate snippets and pages from JSON or YAML [#generate-snippets-and-pages-from-json-or-yaml]

If you store data in a JSON or YAML file, generate snippets from that source data. Use a script to write the data snippet with one page per entry and create the matching navigation group. Run the script in CI whenever the source file changes and commit the result.

<Steps>
  <Step title="Write the generator">
    This script reads `sdk-components.yaml`, writes the snippet from the previous example, creates a page for each component, and replaces the pages of the navigation group named "Components" in `docs.json`.

    ```js title="scripts/generate-docs.mjs"
    import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
    import { parse } from "yaml";

    const components = parse(readFileSync("sdk-components.yaml", "utf8"));
    const slug = (name) => name.toLowerCase().replace(/[^a-z0-9]+/g, "-");

    // One snippet with all the data, for tables and lists anywhere in the docs.
    writeFileSync("snippets/sdk-components.js", `export const sdkComponents = ${JSON.stringify(components, null, 2)};\n`);

    // One page per component.
    mkdirSync("components", { recursive: true });
    for (const component of components) {
      const page = `---
    title: ${JSON.stringify(component.name)}
    description: ${JSON.stringify(component.description)}
    ---

    {/* Generated from sdk-components.yaml by scripts/generate-docs.mjs. Edit the YAML, not this file. */}

    | Field | Value |
    | --- | --- |
    | Version | \`${component.version}\` |
    | Status | ${component.status} |
    `;
      writeFileSync(`components/${slug(component.name)}.mdx`, page);
    }

    // Keep the navigation in sync: replace the pages of the group named "Components", wherever it sits.
    const docs = JSON.parse(readFileSync("docs.json", "utf8"));
    const findGroup = (node) => (Array.isArray(node) ? node.map(findGroup).find(Boolean) : node && typeof node === "object" ? (node.group === "Components" ? node : findGroup(Object.values(node))) : undefined);
    const group = findGroup(docs.navigation);
    if (group) {
      group.pages = components.map((component) => `components/${slug(component.name)}`);
      writeFileSync("docs.json", `${JSON.stringify(docs, null, 2)}\n`);
    }
    ```

    For a JSON source, replace `parse()` with `JSON.parse()` and skip the `yaml` dependency. Running the script twice produces identical files, so it is safe to run on every push.
  </Step>

  <Step title="Run it in a GitHub Action">
    The workflow runs when the source file or the script changes, then commits whatever the script produced. The default `GITHUB_TOKEN` does not trigger other workflows when it pushes, so the job cannot loop. Mintlify deploys the push like any other commit.

    ```yaml title=".github/workflows/generate-docs.yml"
    name: Generate docs from YAML

    on:
      push:
        paths:
          - sdk-components.yaml
          - scripts/generate-docs.mjs
      workflow_dispatch:

    permissions:
      contents: write

    jobs:
      generate:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - uses: actions/setup-node@v4
            with:
              node-version: 22
          - run: npm install yaml
          - run: node scripts/generate-docs.mjs
          - name: Commit generated files
            run: |
              git add -A
              if git diff --cached --quiet; then
                echo "Nothing changed."
                exit 0
              fi
              git config user.name "github-actions[bot]"
              git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
              git commit -m "docs: regenerate from sdk-components.yaml"
              git push
    ```

    If you store the source file in another repository, run the workflow there instead. Check out the docs repository with a token that can push to it, run the script, and commit.
  </Step>
</Steps>
