Fern Components
Fern ships a built-in library of ~27 MDX components you can use in documentation pages without
importing anything. This skill catalogs them, says when each is worth reaching for, and points to
references/components-reference.md for exact syntax, every prop,
and copy-paste examples.
This skill is component knowledge only. Page placement, docs/index.yml nav, frontmatter, SPDX
headers, links, terminology, and the style guide belong to the dynamo-docs skill — use both
together when authoring.
The .md vs .mdx rule (read this first in this repo)
Fern components are JSX. They render only in .mdx files. This repo (ai-dynamo/dynamo) mixes two
page formats, and the boundary is a hard must-fix:
| Page type |
How to write rich content |
.mdx (e.g. kubernetes/getting-started/introduction.mdx, recipe pages) |
Use Fern components directly — <CardGroup>, <Steps>, <Accordion>, <Tabs>, <Note>, etc. |
.md (most docs pages) |
Do not hand-write <Note>/<Tip>/etc. Write callouts GitHub-style (> [!NOTE]); docs/fern/scripts/convert_callouts.py converts them at build. Other components (Cards, Steps, Tabs…) are not available — restructure with plain Markdown, or convert the page to .mdx on purpose. |
Callout conversion map (GitHub → Fern), for .md pages:
> [!NOTE] |
> [!TIP] |
> [!IMPORTANT] |
> [!WARNING] |
> [!CAUTION] |
<Note> |
<Tip> |
<Info> |
<Warning> |
<Error> |
Before using any component below, confirm the target file is .mdx. If it's .md and you need a
non-callout component, the decision (restructure vs. rename to .mdx) is a dynamo-docs / nav concern —
raise it, don't silently rename.
When to reach for a component (and when not)
Prefer plain Markdown. A component earns its place only when it does something Markdown can't:
progressive disclosure, sequencing, branching, live API data, or interactivity. Don't decorate — an
ordered list is better than <Steps> for two trivial steps, and a sentence is better than a <Card>
whose only job is to host a link (see the dynamo-docs / Fern cross-reference guidance).
| You want to… |
Component |
Notes |
| Flag a note / warning / tip |
Callout (<Note> <Tip> <Warning> <Info> <Success> <Error> <Launch> <Check>) |
In .md, use > [!NOTE] syntax instead (see above). |
| Collapse FAQs / optional detail |
Accordion / AccordionGroup |
Content stays SEO-indexed while collapsed. |
| Sequence a tutorial / setup |
Steps / Step |
Auto-numbered, anchor links. Use toc to surface in the TOC. |
| Show the same thing per-language / per-OS |
Tabs / Tab |
language= syncs all tabs+code blocks site-wide. |
| Navigation grid / feature hub |
Card / CardGroup |
cols={n}, Font Awesome icons, images, href makes the whole card clickable. |
| Rich code (highlight, focus, title, embed a file) |
Code block / <Code> / <CodeBlocks> / <CodeGroup> |
Fenced ``` with attrs; <Code src> embeds local/GitHub files. |
| Multiple install commands (npm/pnpm/yarn) |
CodeGroup with for= |
Custom sync group independent of language. |
| Image with caption / framing |
Frame |
Wraps <img>/<video>; background="subtle". |
| Long / searchable / sticky-header table |
StickyTable / SearchableTable / StickySearchableTable, or <table sticky searchable> |
Plain Markdown tables are fine for short data. |
| Inline status / version chip |
Badge |
For longer notes use a Callout instead. |
| Small icon inline / in headings |
Icon |
Font Awesome name or ./path.svg. |
| Clickable button / CTA / download trigger |
Button |
intent, href, icons. |
| Downloadable asset (PDF, ZIP bundle) |
Download |
src= single file, sources={[…]} zips multiple. |
| Click-to-copy inline text |
Copy |
Show one value, copy another via clipboard=. |
| Hover explanation for a term or code token |
Tooltip / <Template> |
<Template> adds tooltips to code-block variables. |
| Document a param / field / config key |
ParamField |
The standard field-doc row: path, type, required, default, deprecated. |
| Indent nested params visually |
Indent |
Wraps any content (unlike <Folder>). |
| Show a project / directory tree |
Files / Folder / File |
defaultOpen, href, highlighted, comment. |
| Link to non-heading content |
Anchor |
id= on paragraphs, tables, code blocks. |
| Float supplementary content right |
Aside |
Sticky; good for an endpoint snippet beside prose. |
| Copyable AI prompt (open in Cursor/Claude/ChatGPT) |
Prompt |
actions={["cursor","claude","chatgpt"]} or custom URL. |
| Show/hide by product, version, or role |
If |
products / versions / roles, combinable, not to invert. |
| Inline versioned content with a switcher |
Versions / Version |
Distinct from site-wide versioning. |
| API reference — request code sample |
EndpointRequestSnippet |
endpoint="POST /path"; languages, payload, hideTryItButton. |
| API reference — response sample |
EndpointResponseSnippet |
Pulls from your API definition. |
| API reference — endpoint schema (params/body) |
EndpointSchemaSnippet |
selector="request.body" etc. |
| API reference — any named type |
Schema / SchemaSnippet |
<Schema type="…"> fields; <SchemaSnippet> JSON. |
| API reference — live "try it" request builder |
RunnableEndpoint |
Real HTTP calls from the page. |
| API reference — webhook payload |
WebhookPayloadSnippet |
By operationId. |
| Reuse a Markdown fragment in many places |
<Markdown src> (reusable snippets) |
Single-source constants/warnings; supports {{params}}. |
| Something bespoke / interactive |
Custom React component |
.tsx in a components dir wired via docs.yml; SSR'd. |
Full syntax, every prop, variants, and examples for all of the above are in
references/components-reference.md. Read the relevant section
before writing a component you don't use often — props and exact names (e.g. <Note> vs <Callout intent>, iconSize math, selector values) are easy to get wrong from memory.
Authoring workflow
- Confirm the file is
.mdx. If .md, only GitHub-style callouts apply (see the rule above).
- Ask "does Markdown already do this?" If yes, use Markdown. Only reach for a component when it
adds disclosure, sequencing, branching, interactivity, or live API data.
- Open the reference section for the component and copy the closest example. Match prop names and
casing exactly (MDX is
className, not class; attributes are camelCase — autoPlay, not
autoplay).
- Nest deliberately. Most containers accept rich children (a
<Note> inside a <Step>, a
<CardGroup> inside an <Accordion>). <Folder> is the exception — it takes only <File> /
<Folder>; use <Indent> when you need to indent anything else.
- Anchors:
##/### headings auto-generate anchors; title= props on <Step>, <Tab>,
<Accordion>, <Card> do not. To deep-link one, add a real heading or use <Anchor id>.
- MDX gotchas that break the build: put a blank line after
<div …> and before </div>; keep
code fences at column 0; replace bare <https://…> with [text](https://…); escape stray </>
and literal $ (\$) outside code.
Suggesting components during review
When reviewing or improving an existing .mdx page, watch for these high-value swaps:
- A wall of "first… then… next…" prose or a long ordered list of actions → Steps.
- Parallel per-language / per-OS / per-backend blocks repeated back to back → Tabs (with
language= when it's code, so they sync).
- A long FAQ, or optional deep-dive detail interrupting the main flow → Accordion.
- A cluster of "see also" links at the top or bottom of a page → CardGroup of Cards.
- Hand-maintained request/response code that duplicates the API definition → the Endpoint*Snippet
family (also gives AI agents structured Markdown output).
- The same constant, warning, or boilerplate copy-pasted across pages → a reusable snippet
(
<Markdown src>).
- A very long reference table users scan → StickyTable / SearchableTable.
Suggest the lightest option, name the component, and point at the reference section — don't rewrite a
working page just to add components.
Staying in sync with Fern (refresh mechanism)
This skill is a cached snapshot of Fern's component docs, not a live mirror — so it works offline
and loads instantly. The trade-off is that it can drift when Fern ships or changes a component.
scripts/refresh_components.py tracks that drift, and
manifest.json records the upstream git-blob SHA of every source page as of the last
sync (see manifest.json's fetched_at for how old this snapshot is).
Source of truth (GitHub fern-api/docs@main): the component-library pages under
fern/products/docs/pages/component-library/** plus customization/custom-react-components.mdx and
navigation/tabs.mdx.
On-load staleness check (do this when the skill loads): read manifest.json's fetched_at. If it
is more than ~3 months old — or the user asks whether the components are current, or a component
doesn't behave as the reference claims — tell the user the snapshot's age and offer to refresh. Don't
refresh unprompted: it needs internet and edits the reference, so surface it and let the user say go.
Refreshing prefers the gh CLI and falls back to the public GitHub API; everything else here is
offline.
# From the skill dir. --check is read-only and safe to run anytime.
python3 scripts/refresh_components.py --check # drift vs manifest? exit 2 = drift, 0 = in sync
python3 scripts/refresh_components.py --fetch --out /tmp/fern-refresh # download only changed pages
# → then update references/components-reference.md and the tables above from those pages,
python3 scripts/refresh_components.py --sync # record the new SHAs as current
Only --sync writes the manifest; --check/--fetch never mutate the skill. After editing the
reference from freshly fetched pages, run --sync so the next --check is clean.
1---2name: fern-components3description: Knowledge of Fern's built-in MDX component library (accordions, callouts, cards, steps, tabs, code blocks, API-reference snippets, and more) for authoring docs pages. Use when writing or editing a Fern `.mdx` page and deciding whether a component would present content better than plain Markdown, when a user asks "what Fern components exist" or "how do I use `<X>`", or when reviewing a page for missed opportunities to use a component. Complements dynamo-docs (which owns page placement, nav, frontmatter, and the style guide).4license: Apache-2.05---67# Fern Components89<!--10SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.11SPDX-License-Identifier: CC-BY-4.012-->1314Fern ships a built-in library of ~27 MDX components you can use in documentation pages without15importing anything. This skill catalogs them, says **when each is worth reaching for**, and points to16[`references/components-reference.md`](references/components-reference.md) for exact syntax, every prop,17and copy-paste examples.1819This skill is **component knowledge only**. Page placement, `docs/index.yml` nav, frontmatter, SPDX20headers, links, terminology, and the style guide belong to the **dynamo-docs** skill — use both21together when authoring.2223## The `.md` vs `.mdx` rule (read this first in this repo)2425Fern components are JSX. They render **only in `.mdx` files**. This repo (`ai-dynamo/dynamo`) mixes two26page formats, and the boundary is a hard must-fix:2728| Page type | How to write rich content |29|---|---|30| **`.mdx`** (e.g. `kubernetes/getting-started/introduction.mdx`, recipe pages) | Use Fern components **directly** — `<CardGroup>`, `<Steps>`, `<Accordion>`, `<Tabs>`, `<Note>`, etc. |31| **`.md`** (most docs pages) | **Do not hand-write `<Note>`/`<Tip>`/etc.** Write callouts GitHub-style (`> [!NOTE]`); `docs/fern/scripts/convert_callouts.py` converts them at build. Other components (Cards, Steps, Tabs…) are **not** available — restructure with plain Markdown, or convert the page to `.mdx` on purpose. |3233Callout conversion map (GitHub → Fern), for `.md` pages:3435| `> [!NOTE]` | `> [!TIP]` | `> [!IMPORTANT]` | `> [!WARNING]` | `> [!CAUTION]` |36|---|---|---|---|---|37| `<Note>` | `<Tip>` | `<Info>` | `<Warning>` | `<Error>` |3839**Before using any component below, confirm the target file is `.mdx`.** If it's `.md` and you need a40non-callout component, the decision (restructure vs. rename to `.mdx`) is a dynamo-docs / nav concern —41raise it, don't silently rename.4243## When to reach for a component (and when not)4445Prefer plain Markdown. A component earns its place only when it does something Markdown can't:46progressive disclosure, sequencing, branching, live API data, or interactivity. Don't decorate — an47ordered list is better than `<Steps>` for two trivial steps, and a sentence is better than a `<Card>`48whose only job is to host a link (see the dynamo-docs / Fern cross-reference guidance).4950| You want to… | Component | Notes |51|---|---|---|52| Flag a note / warning / tip | **Callout** (`<Note>` `<Tip>` `<Warning>` `<Info>` `<Success>` `<Error>` `<Launch>` `<Check>`) | In `.md`, use `> [!NOTE]` syntax instead (see above). |53| Collapse FAQs / optional detail | **Accordion** / **AccordionGroup** | Content stays SEO-indexed while collapsed. |54| Sequence a tutorial / setup | **Steps** / **Step** | Auto-numbered, anchor links. Use `toc` to surface in the TOC. |55| Show the same thing per-language / per-OS | **Tabs** / **Tab** | `language=` syncs all tabs+code blocks site-wide. |56| Navigation grid / feature hub | **Card** / **CardGroup** | `cols={n}`, Font Awesome icons, images, `href` makes the whole card clickable. |57| Rich code (highlight, focus, title, embed a file) | **Code block** / `<Code>` / `<CodeBlocks>` / `<CodeGroup>` | Fenced ``` with attrs; `<Code src>` embeds local/GitHub files. |58| Multiple install commands (npm/pnpm/yarn) | **CodeGroup** with `for=` | Custom sync group independent of language. |59| Image with caption / framing | **Frame** | Wraps `<img>`/`<video>`; `background="subtle"`. |60| Long / searchable / sticky-header table | **StickyTable** / **SearchableTable** / **StickySearchableTable**, or `<table sticky searchable>` | Plain Markdown tables are fine for short data. |61| Inline status / version chip | **Badge** | For longer notes use a Callout instead. |62| Small icon inline / in headings | **Icon** | Font Awesome name or `./path.svg`. |63| Clickable button / CTA / download trigger | **Button** | `intent`, `href`, icons. |64| Downloadable asset (PDF, ZIP bundle) | **Download** | `src=` single file, `sources={[…]}` zips multiple. |65| Click-to-copy inline text | **Copy** | Show one value, copy another via `clipboard=`. |66| Hover explanation for a term or code token | **Tooltip** / `<Template>` | `<Template>` adds tooltips to code-block variables. |67| Document a param / field / config key | **ParamField** | The standard field-doc row: `path`, `type`, `required`, `default`, `deprecated`. |68| Indent nested params visually | **Indent** | Wraps any content (unlike `<Folder>`). |69| Show a project / directory tree | **Files** / **Folder** / **File** | `defaultOpen`, `href`, `highlighted`, `comment`. |70| Link to non-heading content | **Anchor** | `id=` on paragraphs, tables, code blocks. |71| Float supplementary content right | **Aside** | Sticky; good for an endpoint snippet beside prose. |72| Copyable AI prompt (open in Cursor/Claude/ChatGPT) | **Prompt** | `actions={["cursor","claude","chatgpt"]}` or custom URL. |73| Show/hide by product, version, or role | **If** | `products` / `versions` / `roles`, combinable, `not` to invert. |74| Inline versioned content with a switcher | **Versions** / **Version** | Distinct from site-wide versioning. |75| **API reference — request code sample** | **EndpointRequestSnippet** | `endpoint="POST /path"`; `languages`, `payload`, `hideTryItButton`. |76| **API reference — response sample** | **EndpointResponseSnippet** | Pulls from your API definition. |77| **API reference — endpoint schema (params/body)** | **EndpointSchemaSnippet** | `selector="request.body"` etc. |78| **API reference — any named type** | **Schema** / **SchemaSnippet** | `<Schema type="…">` fields; `<SchemaSnippet>` JSON. |79| **API reference — live "try it" request builder** | **RunnableEndpoint** | Real HTTP calls from the page. |80| **API reference — webhook payload** | **WebhookPayloadSnippet** | By `operationId`. |81| Reuse a Markdown fragment in many places | **`<Markdown src>`** (reusable snippets) | Single-source constants/warnings; supports `{{params}}`. |82| Something bespoke / interactive | **Custom React component** | `.tsx` in a components dir wired via `docs.yml`; SSR'd. |8384Full syntax, every prop, variants, and examples for **all** of the above are in85[`references/components-reference.md`](references/components-reference.md). Read the relevant section86before writing a component you don't use often — props and exact names (e.g. `<Note>` vs `<Callout87intent>`, `iconSize` math, `selector` values) are easy to get wrong from memory.8889## Authoring workflow90911. **Confirm the file is `.mdx`.** If `.md`, only GitHub-style callouts apply (see the rule above).922. **Ask "does Markdown already do this?"** If yes, use Markdown. Only reach for a component when it93 adds disclosure, sequencing, branching, interactivity, or live API data.943. **Open the reference** section for the component and copy the closest example. Match prop names and95 casing exactly (MDX is `className`, not `class`; attributes are `camelCase` — `autoPlay`, not96 `autoplay`).974. **Nest deliberately.** Most containers accept rich children (a `<Note>` inside a `<Step>`, a98 `<CardGroup>` inside an `<Accordion>`). `<Folder>` is the exception — it takes only `<File>` /99 `<Folder>`; use `<Indent>` when you need to indent anything else.1005. **Anchors:** `##`/`###` headings auto-generate anchors; `title=` props on `<Step>`, `<Tab>`,101 `<Accordion>`, `<Card>` **do not**. To deep-link one, add a real heading or use `<Anchor id>`.1026. **MDX gotchas that break the build:** put a blank line after `<div …>` and before `</div>`; keep103 code fences at column 0; replace bare `<https://…>` with `[text](https://…)`; escape stray `<`/`>`104 and literal `$` (`\$`) outside code.105106## Suggesting components during review107108When reviewing or improving an existing `.mdx` page, watch for these high-value swaps:109110- A wall of "first… then… next…" prose or a long ordered list of actions → **Steps**.111- Parallel per-language / per-OS / per-backend blocks repeated back to back → **Tabs** (with112 `language=` when it's code, so they sync).113- A long FAQ, or optional deep-dive detail interrupting the main flow → **Accordion**.114- A cluster of "see also" links at the top or bottom of a page → **CardGroup** of **Card**s.115- Hand-maintained request/response code that duplicates the API definition → the **Endpoint*Snippet**116 family (also gives AI agents structured Markdown output).117- The same constant, warning, or boilerplate copy-pasted across pages → a **reusable snippet**118 (`<Markdown src>`).119- A very long reference table users scan → **StickyTable** / **SearchableTable**.120121Suggest the lightest option, name the component, and point at the reference section — don't rewrite a122working page just to add components.123124## Staying in sync with Fern (refresh mechanism)125126This skill is a **cached snapshot** of Fern's component docs, not a live mirror — so it works offline127and loads instantly. The trade-off is that it can drift when Fern ships or changes a component.128[`scripts/refresh_components.py`](scripts/refresh_components.py) tracks that drift, and129[`manifest.json`](manifest.json) records the upstream git-blob SHA of every source page as of the last130sync (see `manifest.json`'s `fetched_at` for how old this snapshot is).131132Source of truth (GitHub `fern-api/docs@main`): the component-library pages under133`fern/products/docs/pages/component-library/**` plus `customization/custom-react-components.mdx` and134`navigation/tabs.mdx`.135136**On-load staleness check (do this when the skill loads):** read `manifest.json`'s `fetched_at`. If it137is more than ~3 months old — or the user asks whether the components are current, or a component138doesn't behave as the reference claims — tell the user the snapshot's age and offer to refresh. Don't139refresh unprompted: it needs internet and edits the reference, so surface it and let the user say go.140Refreshing prefers the `gh` CLI and falls back to the public GitHub API; everything else here is141offline.142143```bash144# From the skill dir. --check is read-only and safe to run anytime.145python3 scripts/refresh_components.py --check # drift vs manifest? exit 2 = drift, 0 = in sync146python3 scripts/refresh_components.py --fetch --out /tmp/fern-refresh # download only changed pages147# → then update references/components-reference.md and the tables above from those pages,148python3 scripts/refresh_components.py --sync # record the new SHAs as current149```150151Only `--sync` writes the manifest; `--check`/`--fetch` never mutate the skill. After editing the152reference from freshly fetched pages, run `--sync` so the next `--check` is clean.