Skip to content
Margin
Create content

Reuse content with snippets

Choose ingest-time MDX fragments or admitted local code components.

Store shared content under snippets/ at the docs root, then reference it from each page that needs it. Changes take effect when the docs are ingested again; a snippet is not a separately served page.

Choose a snippet type

Use an MDX fragment for repeated instructions, callouts, or code examples. Margin expands its content into the consuming page at ingest, so the text is part of the page rather than a runtime component.

Use a local JavaScript or TypeScript component when the shared content needs executable behavior, such as a counter. These modules must pass compilation and import restrictions before Margin includes them in a page. They execute in the visitor’s browser or a restricted render isolate, never in the application’s server process.

SourceReference from a pageBehavior
snippets/**/*.mdx (also .md)Default import and JSX tag, or <Snippet file="…" />Fragment expanded at ingest
Named component export in an MDX snippetNamed import and JSX tagCompiled through the code-component path
snippets/**/*.js, .jsx, .ts, .tsxDefault or named import and JSX tagLocal code compiled into the page

Reuse an MDX fragment

Create snippets/retry-note.mdx with the shared text. A fragment does not need an explicit default export.

snippets/retry-note.mdx
<Note>
  Before retrying a write, check whether the first request succeeded.
</Note>

Import the fragment from a consuming page and render the imported name as a JSX tag. Include the file extension in the import path.

guides/create-record.mdx
---
title: "Create a record"
---

import RetryNote from '/snippets/retry-note.mdx'

Check the write response before proceeding.

<RetryNote />

A second page can use the built-in Snippet component instead of an import. Its file attribute must be a literal string.

guides/update-record.mdx
---
title: "Update a record"
---

Check the update response before proceeding.

<Snippet file="retry-note.mdx" />

The file value is resolved under snippets/. A leading /snippets/ or snippets/ prefix is also accepted. If the extension is omitted, Margin tries .mdx before .md; directory traversal with .. is rejected.

For a parameterized fragment, use an imported JSX tag with static props. A string, boolean attribute, or statically evaluated expression such as count={3} can replace a matching {count} expression in the fragment, including inside fenced code. Dynamic props are not resolved at ingest.

Ingest both consuming pages, then open them and confirm that each contains the retry note. Edit the fragment and ingest again to update both copies.

Use a local code component

Create snippets/Counter.tsx with a default-exported component. React hooks are available by name without importing react.

snippets/Counter.tsx
export default function Counter() {
  const [count, setCount] = useState(0)
  return (
    <button onClick={() => setCount(count + 1)}>
      Count: {count}
    </button>
  )
}

Import the module into a page and invoke it as a JSX component.

counter.mdx
---
title: "Try the counter"
---

import Counter from '/snippets/Counter.tsx'

Click the button to increment its count.

<Counter />

Named exports use named imports, such as import { Counter } from '/snippets/Counter.tsx' for a module that exports Counter by name. Named component exports from .mdx snippets use this same executable path; a default MDX import still means fragment transclusion. Static exported constants from MDX snippets can resolve at ingest instead of requiring runtime execution.

After ingest, open the counter page. The button should start at Count: 0 and change to Count: 1 after one click.

Understand execution limits

Snippet compilation is not permission to run arbitrary server-side code. Compiled page code can run in the browser; its server-rendered snapshot runs only in a host-less V8 isolate. The isolate has no process, require, fetch, timers, DOM, or filesystem access. Its render budget is 250 ms with a 128 MB heap cap. A declined isolate render retains the client-only rendering path rather than granting host access.

Keep render-time code independent of browser globals. Browser-only work belongs in an effect or event handler so it does not require a DOM while the isolate produces markup. Do not put secrets in snippet source: executable page content reaches the visitor’s browser.

Admission ruleAcceptedRejected or unavailable
Code-module locationFiles under snippets/ with .js, .jsx, .ts, or .tsx extensionsArbitrary application modules outside snippets/
Imports in code modulesExplicit /snippets/… or snippets/… module pathsRelative paths, package imports, URL imports, and Node built-ins
Exports consumed by pagesDefault or named exports that exist in the moduleNamespace imports such as import * as Widgets
React supportAmbient React, JSX runtime, and supported hooksInstalling a dependency does not admit it as a snippet import
Documentation componentsBuilt-in components such as Note and Card, used without importsImporting UI packages to supply components

The supported ambient hooks are useState, useEffect, useMemo, useCallback, useRef, useReducer, useContext, useId, useTransition, and useSyncExternalStore. JSX runtime support is provided by Margin; no react/jsx-runtime import is needed in authored snippets.

Fix admission failures

Inspect ingest diagnostics even if the page still serves. Missing imported fragments and rejected code modules can leave a served page without the snippet’s content.

Diagnostic or exact messageCauseCorrection
snippet_missingAn imported file or named MDX export was not foundMatch the full path, extension, and exported name
js_snippet_rejectedA code module or MDX component export failed compilationFix syntax and imports in the indicated module; keep dependencies under /snippets/
namespace imports from snippets are not supported: WidgetsA page uses import * as WidgetsImport a default or named component directly
<Snippet> requires a literal "file" attributeThe file attribute is missing or computedUse a literal such as file="retry-note.mdx"
snippet path may not traverse directories: ../retry-note.mdxA Snippet path includes ..Reference the file from the snippets/ root
missing snippet file: retry-note.mdxA Snippet reference cannot resolve its fragmentAdd the fragment or correct the file value

The missing-file warning for an import and the missing-file validation failure for Snippet are different diagnostics. After correcting either reference, ingest again and verify the actual content on each consuming page, not only that the page loads.