Home /

docs

Components

Pass custom React components that become available in all MDX files — no imports needed.

How It Works

When you register components in docs.config.ts, they're merged into the MDX component map via getMDXComponents(). The merge order is:

  1. Built-in components — headings, code blocks, callouts, Tab, Tabs, HoverLink, etc.
  2. Theme defaults — from theme.ui.components for built-ins like HoverLink and Prompt
  3. Your components — from docs.config.ts (overrides built-ins if names match)

This means you can both add new components and override existing ones.


Creating a Custom Component

1. Create the component

components/info-card.tsx
interface InfoCardProps {
  title: string;
  children: React.ReactNode;
  variant?: "default" | "tip" | "warning";
}

export function InfoCard({ title, children, variant = "default" }: InfoCardProps) {
  const colors = {
    default: "border-white/10 bg-white/[0.02]",
    tip: "border-emerald-500/20 bg-emerald-500/[0.04]",
    warning: "border-amber-500/20 bg-amber-500/[0.04]",
  };

  return (
    <div className={`border p-4 my-4 ${colors[variant]}`}>
      <p className="text-sm font-medium mb-1">{title}</p>
      <div className="text-sm text-fd-muted-foreground">{children}</div>
    </div>
  );
}

2. Register in config

docs.config.tsx
import { defineDocs } from "@farming-labs/docs";
import { pixelBorder } from "@farming-labs/theme/pixel-border";
import { InfoCard } from "./components/info-card";

export default defineDocs({
  // ...theme, nav, etc.
  components: {
    InfoCard,
  },
});

3. Use in any MDX file

No import needed — just use it directly:

app/docs/getting-started/page.mdx
# Getting Started

<InfoCard title="Quick Tip" variant="tip">
  You can use custom components anywhere without importing them.
</InfoCard>

Overriding Built-in Components

You can replace any built-in MDX component by registering one with the same name. Your component will take precedence.

Available built-in components

The following components are available by default through getMDXComponents(). You can use them in MDX without imports or docs.config.ts registration:

  • h1, h2, h3, h4, h5, h6 — heading elements
  • p, a, ul, ol, li — text and list elements
  • table, thead, tbody, tr, th, td — table elements
  • pre, code — code blocks and inline code
  • img — images
  • blockquote — blockquotes
  • hr — horizontal rules
  • Agent — optional shorthand for content that only appears in agent-projected output
  • Human — shorthand for content that only appears in human-projected output
  • Audience — explicit audience wrapper with only="human" or only="agent"
  • HoverLink — hover-triggered popover link card with title, description, and CTA
  • Prompt — reusable AI prompt card with copy/open actions
  • Tab, Tabs — tabbed content
  • CodeGroup — Mintlify-compatible tabbed code groups powered by the built-in code block UI
  • Callout — callout/admonition boxes

Example: Tailor content by audience

Audience wrappers are built in, so they do not need to be registered in docs.config.ts:

page.mdx
Shared setup instructions.

<Human>Use the highlighted control in the screenshot.</Human>

<Agent>Run `pnpm verify` and require exit code 0.</Agent>

<Audience only="human">This is the explicit form of `Human`.</Audience>

<Audience only="agent">This is the explicit form of `Agent`.</Audience>

Rendered HTML and public search use the human projection. Markdown, Ask AI, MCP, llms-full.txt, and static agent exports use the agent projection; compact llms.txt links point to that Markdown. Sitemaps contain route metadata rather than page bodies. Content outside a wrapper is shared, and <Agent> remains entirely optional. Keep Audience.only as a static literal and do not pass audience props through a spread; docs review reports declarations that static outputs cannot resolve consistently.

These wrappers are not authentication or authorization. Never place secrets in them. See Agent Primitive for the complete surface matrix and agent.md override behavior.

Example: Use CodeGroup

CodeGroup lets you wrap multiple fenced code blocks in one tabbed block. The tab label comes from title, filename, or the bare filename after the language, so Mintlify-style docs can move over without custom components or MDX component registration.

page.mdx
<CodeGroup>

```bash npm
npm install @farming-labs/docs
```

```bash pnpm
pnpm add @farming-labs/docs
```

```ts title="docs.config.ts"
import { defineDocs } from "@farming-labs/docs";

export default defineDocs({
  entry: "app/docs",
});
```

</CodeGroup>

Use <CodeGroup dropdown> when you want to preserve Mintlify-authored MDX that includes the dropdown prop. It is accepted by the renderer and exposed as a data attribute for theme styling.

HoverLink gives you an inline trigger that opens a richer popover card on hover or focus. It is useful when you want to reference another page without breaking the reader's flow immediately.

page.mdx
<HoverLink
  href="/docs/installation"
  title="Installation"
  description="Set up @farming-labs/docs in a new or existing app with the CLI or the manual steps."
  linkLabel="Read the guide"
>
  installation guide
</HoverLink>

Live example:

Theme defaults vs config overrides

Use theme.ui.components when you want to keep a built-in like HoverLink or Prompt but change its default props across the site. Use components.HoverLink or components.Prompt when you want to replace the component entirely.

docs.config.tsx
import { defineDocs } from "@farming-labs/docs";
import { pixelBorder } from "@farming-labs/theme/pixel-border";

export default defineDocs({
  theme: pixelBorder({
    ui: {
      components: {
        HoverLink: {
          linkLabel: "Open guide",
          showIndicator: false,
          align: "start",
        },
      },
    },
  }),
});
docs.config.tsx
import { defineDocs } from "@farming-labs/docs";
import { MyHoverLink } from "./components/my-hover-link";

export default defineDocs({
  components: {
    HoverLink: MyHoverLink,
  },
});

Example: Use Prompt

Prompt turns a reusable AI prompt into a first-class docs component. By default, the prompt body is treated as payload for copy/open actions rather than visible page content, so the card stays compact like Mintlify-style prompt blocks.

docs.config.tsx
import { ArrowUpRight, Check, Copy, Sparkles } from "lucide-react";
import { defineDocs } from "@farming-labs/docs";
import { pixelBorder } from "@farming-labs/theme/pixel-border";

export default defineDocs({
  icons: {
    sparkles: <Sparkles size={16} />,
    copy: <Copy size={16} />,
    check: <Check size={16} />,
    arrowUpRight: <ArrowUpRight size={16} />,
  },
  theme: pixelBorder({
    ui: {
      components: {
        Prompt: {
          icon: "sparkles",
          actions: ["copy", "open"],
          providers: ["ChatGPT", "Claude", "Cursor"],
          copyIcon: "copy",
          copiedIcon: "check",
          openIcon: "arrowUpRight",
        },
      },
    },
  }),
  pageActions: {
    openDocs: {
      enabled: true,
      target: "markdown",
      providers: ["chatgpt", "claude", "cursor"],
    },
  },
});
page.mdx
<Prompt
  title="Write agent-friendly docs"
  description="Give contributors a reusable authoring prompt."
>
Write a concise agent-friendly documentation page for this feature.

Include:
- the user problem it solves
- the exact setup steps
- one verification checklist
- one troubleshooting section
</Prompt>

Useful visibility props:

  • showPrompt — render the prompt body inside the card
  • showTitle — hide the title even if title is set
  • showDescription — hide the description even if description is set

Live example:

Write agent-friendly docs

Give contributors a reusable authoring prompt.

If you want the prompt text visible on the page too, opt in:

page.mdx
<Prompt
  title="Visible prompt body"
  description="Show the prompt on the page and still keep copy/open actions."
  showPrompt
>
Write a concise troubleshooting checklist for this feature.
</Prompt>

Live example:

Write agent-friendly docs

Give contributors a reusable authoring prompt.

Write a concise agent-friendly documentation page for this feature.

Include:
- the user problem it solves
- the exact setup steps
- one verification checklist
- one troubleshooting section

Example: Override the a tag

components/custom-link.tsx
import Link from "next/link";

export function CustomLink({
  href,
  children,
  ...props
}: React.AnchorHTMLAttributes<HTMLAnchorElement>) {
  const isExternal = href?.startsWith("http");

  if (isExternal) {
    return (
      <a href={href} target="_blank" rel="noopener noreferrer" {...props}>
        {children} ↗
      </a>
    );
  }

  return (
    <Link href={href || "#"} {...props}>
      {children}
    </Link>
  );
}
docs.config.tsx
components: {
  a: CustomLink,
},

Now every link in your MDX files will use CustomLink — external links open in a new tab with an arrow indicator.

Example: Override blockquote

components/custom-blockquote.tsx
export function CustomBlockquote({ children }: { children: React.ReactNode }) {
  return (
    <blockquote className="border-l-2 border-white/20 pl-4 my-4 text-fd-muted-foreground italic">
      {children}
    </blockquote>
  );
}
docs.config.tsx
components: {
  blockquote: CustomBlockquote,
},

Example: Override pre (code blocks)

components/custom-code-block.tsx
export function CustomPre({ children, ...props }: React.HTMLAttributes<HTMLPreElement>) {
  return (
    <div className="relative group my-4">
      <pre className="border border-white/10 bg-black p-4 overflow-x-auto" {...props}>
        {children}
      </pre>
    </div>
  );
}
docs.config.tsx
components: {
  pre: CustomPre,
},

Multiple Components

Register as many components as you need:

docs.config.tsx
import { InfoCard } from "./components/info-card";
import { CustomLink } from "./components/custom-link";
import { VideoEmbed } from "./components/video-embed";
import { ApiReference } from "./components/api-reference";

export default defineDocs({
  // ...
  components: {
    // Custom components
    InfoCard,
    VideoEmbed,
    ApiReference,

    // Built-in overrides
    a: CustomLink,
  },
});

Then use them all across your MDX without imports:

page.mdx
# API Overview

<InfoCard title="Authentication Required" variant="warning">
  All endpoints require a valid API key.
</InfoCard>

<ApiReference endpoint="/api/users" method="GET" />

<VideoEmbed url="https://youtube.com/watch?v=..." />

Component Props

Components receive their props as defined in your implementation. The only requirement is that they must be valid React components (function or class).

For built-in overrides, your component should accept the same props as the original element (e.g., React.AnchorHTMLAttributes for a, React.HTMLAttributes<HTMLPreElement> for pre).

For custom components, define whatever props interface you need.

Customize
Live preview on these docs
Reset
Customize
Export Code
Preset
Explore more themes
Primary Color
Primary
Colors
Background
Foreground
Muted Text
Border
Card
Muted BG
Ring
Layout
SidebarBordered
TOC StyleDirectional (tree)
TOC DepthH2 + H3
Ask AI
ModeFloating
Floating StyleFull Modal
PositionBottom Right
Features