---
title: Components
description: MDX components the pipeline knows how to flatten into agent-readable markdown.
lastModified: "2026-06-12T08:52:04+01:00"
---
Leadtype does not ship UI components. **Your docs app owns runtime rendering, styling, and accessibility** — it only has to honor a small naming contract so the remark pipeline can flatten each component into markdown for agents, search, and `llms-full.txt`.

## Why flatten at all?

Interactive MDX components like `<Tabs>` or `<Callout>` render fine in a browser but do nothing for an agent reading raw text. *[Flattening](/docs/how-it-works#vocabulary)* converts each component into a portable markdown equivalent at conversion time:

* A `<Callout>` becomes a blockquote.
* A `<Tabs>` becomes a stack of bold headings, one per tab.
* A `<TypeTable>` becomes a markdown table.
* A `<Mermaid>` becomes a fenced ` ```mermaid ` block — the diagram source survives so other tooling can render it.

This means the same content reaches three audiences (humans, agents, search) without you maintaining two copies.

## The naming contract

The remark pipeline recognizes these names. If your components use the same names, flattening Just Works:

`Accordion`, `AccordionItem`, `Audience`, `Callout`, `Card`, `Cards`, `CommandTabs`, `Details`, `Example`, `ExtractedTypeTable`, `File`, `FileTree`, `Folder`, `Mermaid`, `Prompt`, `Section`, `Step`, `Steps`, `Tab`, `Tabs`, `TopicSwitcher`, `TypeTable`.

If your app uses different names — or a component that has no equivalent above — you have two options: rename to match the contract, or define how it flattens with [`defineComponentFlattener`](/docs/reference/remark#custom-component-flatteners). You declare the component name and a `toMarkdown` function; leadtype handles tree-walking, prop parsing, child flattening, and plugin order for you.

A rendered component that's neither in the contract nor has a flattener passes through as **raw JSX** in the generated markdown — agents see noise instead of content. [`leadtype lint`](/docs/reference/lint) catches this: the [`unflattened-component`](/docs/reference/lint#content-link-rules) rule warns on any such component. Components shown inside code fences are examples, not rendered JSX, so they never warn.

Runtime registration, heading IDs, and "On this page" sidebars are docs-app concerns. See [Use the source primitive](/docs/pipeline/use-the-source-primitive) for runtime wiring and [`leadtype/mdx`](/docs/reference/mdx) for the full prop-type contract.

## Reuse shared content

Use include tags when several pages need the same explanation, warning, setup step, or code sample. `leadtype generate` and `leadtype lint` handle includes automatically. Add `remarkInclude` before the default plugin stack in custom conversion scripts:

```ts
import { defaultRemarkPlugins, remarkInclude } from "leadtype/remark";

remarkPlugins: [remarkInclude, ...defaultRemarkPlugins];
```

Include a whole markdown or MDX partial with `src` or text content:

```mdx
<include src="./shared/session-flow.mdx" />

<include>./shared/session-flow.mdx</include>
```

You can also include code files. Leadtype emits them as fenced code blocks:

```mdx
<include src="../examples/session.ts" />

<include src="../examples/env" lang="bash" />
```

To reuse only one block from a larger file, append `#section-id` and wrap that block in a lowercase HTML `section`:

```mdx
<include src="./shared/auth.mdx#session-flow" />
```

```mdx
<section id="session-flow">
  This content can be reused in multiple pages.
</section>
```

Section reuse matches lowercase `<section id="...">` only. A capitalized `<Section id="...">` component is flattened later for markdown output, but it is not an include anchor.

Relative paths resolve from the including file first. Nested includes are supported, so a shared partial can include files next to itself. See [Remark plugins](/docs/reference/remark#remarkinclude) for base paths and plugin details.

## Component reference

Each section below shows the authored MDX and a live render.

### Callout

Wraps supporting context, warnings, or tips. Variants change styling but flatten identically into a blockquote.

```tsx
<Callout title="Heads up" variant="info">
  Body content goes here.
</Callout>
```

> ℹ️ **Info:** **Heads up**
> Callouts wrap supporting context, warnings, or tips. The remark pipeline flattens them into blockquotes so agents still see the content.
>
> ⚠️ **Warning:** **Don't do this**
> Variants like warning, success, error, and tip change the styling but flatten identically.

### Cards

A grid of short, linked entry points. Flattens to a bullet list of links.

```tsx
<Cards>
  <Card title="Convert" description="Turn MDX into agent-friendly markdown." href="/docs/reference/convert" />
</Cards>
```

* [Convert](/docs/reference/convert)
* [Remark](/docs/reference/remark)
* [Search](/docs/reference/search)

### Steps

Numbered walkthroughs. Flattens to an ordered list with bold step titles.

```tsx
<Steps>
  <Step title="Author docs in MDX">Use the components in this list.</Step>
  <Step title="Run the conversion">`leadtype generate` writes flattened markdown.</Step>
</Steps>
```

1. **Author docs in MDX** Use the components in this list as authoring affordances.

2. **Run the conversion** `leadtype generate` writes flattened markdown to `public/docs/`.

3. **Serve both formats** HTML for humans, `.md` for agents — same URL, content negotiated by the `Accept` header.

### Tabs

Group equivalent content. Flattens to bold headings followed by content so agents do not need a JSX-aware renderer to read every variant.

```tsx
<Tabs items={["TanStack Start", "Next.js", "Vite"]}>
  <Tab value="tanstack-start">…</Tab>
  <Tab value="next-js">…</Tab>
  <Tab value="vite">…</Tab>
</Tabs>
```

**tanstack-start**

Use a Vite middleware (dev/preview) and a Nitro middleware (prod) to negotiate the `Accept` header.

**next-js**

Wire content negotiation in middleware.ts and serve `.md` from a route handler.

**vite**

A `configureServer` middleware is enough for static deployments where `.md` files live in `public/`.

### CommandTabs

Package-manager-aware install or run commands. Flattens to a markdown table with one row per manager.

Use `mode="install"` when `command` is a package name, `mode="run"` when `command` is a CLI name, and `mode="create"` for starter commands. Use the `commands` prop for exact per-manager overrides.

```tsx
<CommandTabs command="leadtype" mode="install" />
<CommandTabs command="leadtype lint" mode="run" />
<CommandTabs
  commands={{ npm: "npm install leadtype", pnpm: "pnpm add leadtype" }}
  defaultManager="pnpm"
/>
```

|Package manager|Command|
|:--|:--|
|npm|`npm install leadtype`|
|pnpm|`pnpm add leadtype`|
|yarn|`yarn add leadtype`|
|bun|`bun add leadtype`|

|Package manager|Command|
|:--|:--|
|npm|`npx leadtype lint`|
|pnpm|`pnpm dlx leadtype lint`|
|yarn|`yarn dlx leadtype lint`|
|bun|`bunx leadtype lint`|

### Prompt

Displays an agent-ready prompt with a copy action. Flattening preserves the prompt body as a fenced `prompt` block so copied instructions also survive in `.md`, root `llms-full.txt`, and bundled `AGENTS.md` output.

```tsx
<Prompt title="Copy prompt for your coding agent" description="Use this after generating artifacts.">
  Inspect `public/docs/agent-readability.json`, then wire markdown responses before the HTML docs route.
</Prompt>
```

**Copy prompt for your coding agent**

Use this after generating artifacts.

```prompt
Inspect `public/docs/agent-readability.json`, then wire markdown responses before the HTML docs route.
```

### Audience

Splits browser-only and agent-only guidance without forking the page. Human content renders in the docs UI and is omitted from markdown output; agent content is hidden in the browser and included in generated markdown.

```tsx
<Audience target="human">Click the robot icon in the header.</Audience>
<Audience target="agent">Read generated markdown before editing runtime routes.</Audience>
```

This sentence appears only in generated markdown for agents.

### FileTree

Shows project structure in guides and release instructions. Flattening emits a fenced text tree so agents can read the same hierarchy without JSX.

```tsx
<FileTree root="public">
  <File name="llms.txt" />
  <Folder name="docs">
    <File name="index.md" />
  </Folder>
</FileTree>
```

```text
public/
├── llms.txt
└── docs/
    └── index.md
```

### Accordion

Collapsible details for secondary content. Flattening ignores open/closed state and emits every item — accordions are not a place to hide content from agents.

```tsx
<Accordion>
  <AccordionItem title="When should I use accordions?">
    Use them for supporting details and optional reference material.
  </AccordionItem>
</Accordion>
```

**When should I use accordions?**

Use them for supporting details, troubleshooting notes, and optional reference material. Closed content is still flattened by the remark pipeline.

**Are accordions a good place to hide content from LLMs?**

No. Conversion ignores the open/closed state and emits everything inside.

### TopicSwitcher

Navigation across equivalent docs topics — frameworks, SDKs, runtimes, deployment targets, product areas. Reader-facing only; it does not automatically read LLM topic config.

```tsx
<TopicSwitcher
  label="Framework"
  activeValue="react"
  items={[
    { value: "react", label: "React", href: "/docs/frameworks/react" },
    { value: "vue", label: "Vue", href: "/docs/frameworks/vue" },
  ]}
/>
```

Framework

* [React](/docs/writing/components) — React integration
* [Vue](/docs/writing/components) — Vue integration
* [Svelte](/docs/writing/components) — Svelte integration

### TypeTable and ExtractedTypeTable

`TypeTable` is for explicit prop or type rows you already know. `ExtractedTypeTable` reads a TypeScript file at conversion time and extracts the table from a named type — keep its `path` stable.

```tsx
<TypeTable
  properties={{
    title: { type: "string", required: true, description: "Heading rendered above the body." },
    variant: { type: "CalloutVariant", default: "info", description: "Visual treatment." },
  }}
/>
```

|Property|Type|Description|Default|Required|
|:--|:--|:--|:--|:--:|
|title|string|Heading rendered above the callout body.|-|✅ Required|
|variant|CalloutVariant|Visual treatment for the callout.|info|Optional|
|deprecated|\~\~boolean\~\~ (deprecated)|Marks the row as deprecated.|false|Optional|

### Example

Data-driven preview and source examples. The host component receives code as data; add file loaders or dynamic imports outside leadtype when an example needs app-specific behavior.

```tsx
<Example
  title="Render MDX"
  description="Preview the output and inspect the source."
  filename="mdx-components.tsx"
  language="tsx"
  code={`import { mdxComponents } from "@/components/docs-mdx";`}
>
  <Callout title="Runtime components" variant="success">
    The host app owns styling and runtime components while leadtype owns conversion.
  </Callout>
</Example>
```

### Mermaid

Diagrams authored as plain text. Renders client-side as interactive SVG. Flattening preserves the source as a fenced ` ```mermaid ` block so other tools can render the diagram from the markdown copy.

```tsx
<Mermaid chart={`graph LR
  MDX -->|remark| Markdown
  Markdown -->|llms.txt| Agents`} />
```

```mermaid
graph LR
MDX -->|remark| Markdown
Markdown -->|search index| API
Markdown -->|llms.txt| Agents
```

## Guidelines

* Keep runtime components in your docs app. Leadtype stays out of UI.
* Keep component names stable. Renaming `<Callout>` breaks the flattening contract until you remap it with [`defineComponentFlattener`](/docs/reference/remark#custom-component-flatteners).
* For agent output quality, read the converted `.md` files first and only edit components or plugin order if the markdown actually looks wrong.
