# Components
URL: /docs/customization/components
LLM index: /llms.txt
Description: Override built-in or add custom MDX components

# Components

Use this page when the user asks about this topic: Override built-in or add custom MDX components.
Keep answers grounded in the exact options, routes, commands, and examples documented here.
If the request moves beyond this page, point to the closest related docs instead of inventing config.

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

```tsx title="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

```tsx title="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:

```mdx title="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 (from `fumadocs-ui`):

- `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
- `HoverLink` — hover-triggered popover link card with title, description, and CTA
- `Prompt` — reusable AI prompt card with copy/open actions
- `Tab`, `Tabs` — tabbed content
- `Callout` — callout/admonition boxes

### Example: Use `HoverLink`

`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.

```mdx title="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:

<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>

### 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.

```tsx title="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",
        },
      },
    },
  }),
});
```

```tsx title="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.

```tsx title="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,
      providers: [
        {
          name: "ChatGPT",
          urlTemplate: "https://chatgpt.com/?q=Read+this+documentation:+{url}",
          promptUrlTemplate: "https://chatgpt.com/?q={prompt}",
        },
        {
          name: "Claude",
          urlTemplate: "https://claude.ai/new?q=Read+this+documentation:+{url}",
          promptUrlTemplate: "https://claude.ai/new?q={prompt}",
        },
        {
          name: "Cursor",
          urlTemplate: "https://cursor.com/link/prompt?text=Read+this+documentation:+{url}",
          promptUrlTemplate: "https://cursor.com/link/prompt?text={prompt}",
        },
      ],
    },
  },
});
```

```mdx title="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:

<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>

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

```mdx title="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: 
<Prompt
  title="Write agent-friendly docs"
  description="Give contributors a reusable authoring prompt."
  showPrompt
>
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>

### Example: Override the `a` tag

```tsx title="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>
  );
}
```

```tsx title="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`

```tsx title="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>
  );
}
```

```tsx title="docs.config.tsx"
components: {
  blockquote: CustomBlockquote,
},
```

### Example: Override `pre` (code blocks)

```tsx title="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>
  );
}
```

```tsx title="docs.config.tsx"
components: {
  pre: CustomPre,
},
```

---

## Multiple Components

Register as many components as you need:

```tsx title="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:

```mdx title="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.