---
title: "Components"
description: "Override built-in or add custom MDX components"
canonical_url: "https://docs.farming-labs.dev/docs/customization/components"
markdown_url: "https://docs.farming-labs.dev/docs/customization/components.md"
last_updated: "2018-10-20"
---

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

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

### 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,
      target: "markdown",
      providers: ["chatgpt", "claude", "cursor"],
    },
  },
});
```

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

## Sitemap

See the full [sitemap](/sitemap.md) for all pages.
Docs-scoped sitemap: [/docs/sitemap.md](/docs/sitemap.md).
Well-known sitemap: [/.well-known/sitemap.md](/.well-known/sitemap.md).
