---
title: "Code Block Components"
description: "Code block components provide versatile, interactive rendering pipelines for syntax-highlighted code snippets, supporting both client-side and server-rendered execution environments across Base UI ..."
last_updated: "2026-10-12T07:53:59.981993+00:00"
canonical_url: "https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/ui-layouts/code-block-components"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [packages/base-ui/src/components/codeblock.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.tsx)
- [packages/radix-ui/src/components/codeblock.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.tsx)
- [packages/base-ui/src/components/codeblock.rsc.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.rsc.tsx)
- [packages/asyncapi/src/ui/operation/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/ui/operation/index.tsx)
- [packages/shadcn/src/rsc.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/shadcn/src/rsc.tsx)
- [packages/openapi/src/ui/operation/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/ui/operation/index.tsx)
- [packages/radix-ui/src/layouts/notebook/slots/sidebar.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/layouts/notebook/slots/sidebar.tsx)
- [packages/asyncapi/src/ui/components/codeblock.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/ui/components/codeblock.tsx)
- [packages/base-ui/src/layouts/notebook/slots/sidebar.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/layouts/notebook/slots/sidebar.tsx)
- [packages/radix-ui/src/components/codeblock.rsc.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.rsc.tsx)
- [packages/openapi/src/ui/components/codeblock.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/ui/components/codeblock.tsx)
- [packages/openapi/src/ui/operation/usage-tabs.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/ui/operation/usage-tabs.tsx)
- [packages/asyncapi/src/ui/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/ui/index.tsx)
- [packages/core/src/mdx-plugins/remark-code-tab.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/remark-code-tab.ts)
- [packages/api-docs/src/components/schema/client.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/api-docs/src/components/schema/client.tsx)
- [packages/python/src/components/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/python/src/components/index.tsx)
- [packages/openapi/src/ui/base.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/ui/base.tsx)
- [packages/base-ui/src/components/dynamic-codeblock.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/dynamic-codeblock.tsx)
- [packages/base-ui/src/components/dynamic-codeblock.core.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/dynamic-codeblock.core.tsx)
- [packages/base-ui/src/layouts/notebook/page/slots/toc.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/layouts/notebook/page/slots/toc.tsx)
- [packages/asyncapi/src/ui/operation/message-examples.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/ui/operation/message-examples.tsx)
- [packages/radix-ui/src/layouts/notebook/page/slots/toc.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/layouts/notebook/page/slots/toc.tsx)
- [packages/radix-ui/src/components/dynamic-codeblock.core.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/dynamic-codeblock.core.tsx)
- [packages/sanity/registry/tabs.component.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/sanity/registry/tabs.component.tsx)
- [packages/radix-ui/src/components/dynamic-codeblock.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/dynamic-codeblock.tsx)
- [packages/core/src/mdx-plugins/codeblock-utils.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/codeblock-utils.ts)
- [packages/shadcn/src/manual-installation.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/shadcn/src/manual-installation.ts)
- [packages/core/src/mdx-plugins/rehype-code.core.ts](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/rehype-code.core.ts)
- [packages/openapi/src/ui/operation/request-tabs.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/openapi/src/ui/operation/request-tabs.tsx)
- [packages/radix-ui/src/layouts/notebook/index.tsx](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/layouts/notebook/index.tsx)
</details>

## Overview

Code block components provide versatile, interactive rendering pipelines for syntax-highlighted code snippets, supporting both client-side and server-rendered execution environments across Base UI and Radix UI packages. They solve the complexities of static code representation by offering responsive viewports, line numbering, copy-to-clipboard actions, and integrated tab grouping for multi-file snippets. Through unified remark and rehype transformations, markdown code blocks automatically convert into interactive tabbed structures while parsing custom metadata attributes. Furthermore, specialized integrations in OpenAPI and AsyncAPI leverage these dynamic code block components to render interactive protocol schemas, request examples, and message payloads seamlessly.

Sources: [packages/base-ui/src/components/codeblock.tsx:20-146](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.tsx#L20-L146), [packages/radix-ui/src/components/codeblock.tsx:21-147](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.tsx#L21-L147), [packages/base-ui/src/components/codeblock.rsc.tsx:5-32](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.rsc.tsx#L5-L32)

## Interactive Client Code Block Architecture

### Public API and Component Contracts

The client code block architecture exposes a set of React primitives centered around `CodeBlock`, `Pre`, `CodeBlockTabs`, and `CodeBlockTabsList`. These components manage state transitions for copy actions, tab context propagation, and DOM virtualization for scrollable viewports. The `CodeBlockProps` interface configures options such as titles, icons, copy availability, background retention, and custom toolbar actions.

```typescript
export interface CodeBlockProps extends ComponentProps<'figure'> {
  icon?: ReactNode;
  allowCopy?: boolean;
  keepBackground?: boolean;
  viewportProps?: HTMLAttributes<HTMLElement>;
  'data-line-numbers'?: boolean;
  'data-line-numbers-start'?: number;
  Actions?: (props: { className?: string; children?: ReactNode }) => ReactNode;
}
```

Sources: [packages/base-ui/src/components/codeblock.tsx:20-55](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.tsx#L20-L55), [packages/radix-ui/src/components/codeblock.tsx:21-56](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.tsx#L21-L56)

The component API accepts specific props to control container behavior, styling overrides, and line numbering offsets as detailed in the reference table below.

| Prop | Type | Default | Purpose |
| :--- | :--- | :--- | :--- |
| `icon` | `ReactNode \| string` | `undefined` | Renders a custom icon or HTML string within the header. |
| `allowCopy` | `boolean` | `true` | Enables or disables the clipboard copy action button. |
| `keepBackground` | `boolean` | `false` | Preserves Shiki or Rehype Code generated background colors. |
| `viewportProps` | `HTMLAttributes<HTMLElement>` | `{}` | Passes extra properties directly to the scrollable viewport container. |
| `data-line-numbers` | `boolean` | `undefined` | Toggles CSS line number counters on the code block viewport. |
| `data-line-numbers-start` | `number` | `1` | Sets the starting integer for line number counters. |
| `Actions` | `(props: { className?: string; children?: ReactNode }) => ReactNode` | Custom renderer | Injects custom toolbar action elements or customizes the toolbar wrapper. |

Sources: [packages/base-ui/src/components/codeblock.tsx:20-55](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.tsx#L20-L55), [packages/radix-ui/src/components/codeblock.tsx:21-56](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.tsx#L21-L56)

> [!NOTE]
> When `icon` is passed as a string, `CodeBlock` evaluates it as raw HTML using `dangerouslySetInnerHTML` inside a container with scoped SVG sizing rules (`[&_svg]:size-3.5`), whereas React nodes are rendered directly.

Sources: [packages/base-ui/src/components/codeblock.tsx:99-109](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.tsx#L99-L109), [packages/radix-ui/src/components/codeblock.tsx:99-110](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.tsx#L99-L110)

### DOM Structure and Clipboard Pipeline

The underlying DOM hierarchy maps HTML semantic elements to layout structures. `CodeBlock` renders a `figure` container, enclosing an optional title header with a `figcaption`, an actions toolbar slot, and a scrollable `div` viewport (`role="region"`, `tabIndex={0}`) holding the `Pre` component.

```typescript
export function Pre(props: ComponentProps<'pre'>) {
  return (
    <pre {...props} className={cn('min-w-full w-max *:flex *:flex-col', props.className)}>
      {props.children}
    </pre>
  );
}
```

Sources: [packages/base-ui/src/components/codeblock.tsx:62-68](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.tsx#L62-L68), [packages/radix-ui/src/components/codeblock.tsx:63-69](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.tsx#L63-L69)

The clipboard copy workflow interacts with the DOM by querying the inner `pre` element from the container reference, cloning the node, stripping elements matched by `.nd-copy-ignore` by replacing them with newline characters, and writing `clone.textContent` to `navigator.clipboard`.

```typescript
  const [checked, onClick] = useCopyButton(() => {
    const pre = containerRef.current?.getElementsByTagName('pre').item(0);
    if (!pre) return;

    const clone = pre.cloneNode(true) as HTMLElement;
    clone.querySelectorAll('.nd-copy-ignore').forEach((node) => {
      node.replaceWith('\n');
    });

    void navigator.clipboard.writeText(clone.textContent ?? '');
  });
```

Sources: [packages/base-ui/src/components/codeblock.tsx:157-167](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.tsx#L157-L167), [packages/radix-ui/src/components/codeblock.tsx:158-168](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.tsx#L158-L168)

> [!WARNING]
> Elements tagged with the `.nd-copy-ignore` CSS class inside a `Pre` block are automatically transformed into newline characters during clipboard serialization; forgetting to include this class on non-text visual artifacts like line number pseudo-elements will pollute the copied clipboard payload.

Sources: [packages/base-ui/src/components/codeblock.tsx:162-164](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.tsx#L162-L164), [packages/radix-ui/src/components/codeblock.tsx:163-165](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.tsx#L163-L165)

### Context Propagation and Tab Grouping

Code block tab integration relies on `TabsContext` to coordinate styling and nesting between `CodeBlockTabs`, `CodeBlockTabsList`, and child `CodeBlock` instances. The context tracks whether a code block resides within a tab container and whether the tab container itself is nested.

```typescript
const TabsContext = createContext<{
  containerRef: RefObject<HTMLDivElement | null>;
  nested: boolean;
} | null>(null);
```

Sources: [packages/base-ui/src/components/codeblock.tsx:57-60](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.tsx#L57-L60), [packages/radix-ui/src/components/codeblock.tsx:58-61](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.tsx#L58-L61)

When `inTab` evaluates to `true` via `use(TabsContext) !== null`, `CodeBlock` applies modifier classes (`bg-fd-secondary -mx-px -mb-px last:rounded-b-xl`) instead of standalone card styling (`my-4 bg-fd-card rounded-xl`), ensuring seamless visual integration inside tabbed viewports.

Sources: [packages/base-ui/src/components/codeblock.tsx:81-96](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.tsx#L81-L96), [packages/radix-ui/src/components/codeblock.tsx:82-97](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.tsx#L82-L97)

## Server Code Blocks and Highlighting

### RSC Highlighting Integration

The `ServerCodeBlock` component provides server-side rendering for code blocks by integrating `fumadocs-core/highlight` within React Server Components. It accepts `ServerCodeBlockProps`, combining `HighlightOptions` with raw source code strings and optional configurations for the underlying client component.

```tsx
export type ServerCodeBlockProps = HighlightOptions & {
  code: string;

  /**
   * Extra props for the underlying `<CodeBlock />` component.
   *
   * Ignored if you defined your own `pre` component in `components`.
   */
  codeblock?: CodeBlockProps;
};
```

Sources: [packages/base-ui/src/components/codeblock.rsc.tsx:5-14](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.rsc.tsx#L5-L14), [packages/radix-ui/src/components/codeblock.rsc.tsx:5-14](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.rsc.tsx#L5-L14)

Execution passes raw source strings directly into the highlighter core, overriding default color behavior and injecting a customized `pre` component implementation into the options tree.

```tsx
export async function ServerCodeBlock({ code, codeblock, ...options }: ServerCodeBlockProps) {
  return await highlight(code, {
    defaultColor: false,
    ...options,
    components: {
      pre: (props) => (
        <CodeBlock
          {...props}
          {...codeblock}
          className={cn('my-0', props.className, codeblock?.className)}
        >
          <Pre>{props.children}</Pre>
        </CodeBlock>
      ),
      ...options.components,
    },
  });
```

Sources: [packages/base-ui/src/components/codeblock.rsc.tsx:16-32](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.rsc.tsx#L16-L32), [packages/radix-ui/src/components/codeblock.rsc.tsx:16-32](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.rsc.tsx#L16-L32)

> [!WARNING]
> Defining a custom `pre` component directly within `options.components` suppresses any extra props passed via the `codeblock` property on `ServerCodeBlockProps`.

Sources: [packages/base-ui/src/components/codeblock.rsc.tsx:8-13](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.rsc.tsx#L8-L13), [packages/radix-ui/src/components/codeblock.rsc.tsx:8-13](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.rsc.tsx#L8-L13)

### Execution Trace and Sequence

The rendering and interaction flow operates across server and client boundaries by delegating highlighted output into interactive component wrappers.

1. `ServerCodeBlock` invokes `highlight()` from `fumadocs-core/highlight` with custom syntax options and a wrapped `pre` component renderer.
Sources: [packages/base-ui/src/components/codeblock.rsc.tsx:16-32](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.rsc.tsx#L16-L32), [packages/radix-ui/src/components/codeblock.rsc.tsx:16-32](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.rsc.tsx#L16-L32)
2. `CodeBlock` receives properties from the generated `pre` handler, manages viewport containers, titles, toolbar actions, and copy triggers.
Sources: [packages/base-ui/src/components/codeblock.tsx:70-147](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.tsx#L70-L147)
3. `CopyButton` mounts inside the actions toolbar, referencing the viewport container to extract, clean, and write text content to the clipboard.
Sources: [packages/base-ui/src/components/codeblock.tsx:149-189](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.tsx#L149-L189)

```mermaid
sequenceDiagram
  participant RSC as ServerCodeBlock
  participant CB as CodeBlock
  participant BTN as CopyButton

  RSC->>CB: Renders highlighted syntax inside CodeBlock and Pre
  CB->>BTN: Mounts CopyButton with containerRef pointing to viewport
  BTN->>CB: Queries inner pre element and copies text payload on click
```

Sources: [packages/base-ui/src/components/codeblock.rsc.tsx:16-32](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.rsc.tsx#L16-L32), [packages/base-ui/src/components/codeblock.tsx:70-147](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.tsx#L70-L147), [packages/base-ui/src/components/codeblock.tsx:149-189](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.tsx#L149-L189)

### Server Code Block Configuration Options

| Property | Type | Default Value | Purpose |
| :--- | :--- | :--- | :--- |
| `code` | `string` | *Required* | Raw source code text string supplied for server-side [syntax highlighting](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/core-architecture/syntax-highlighting). |
| `codeblock` | `CodeBlockProps` | `undefined` | Additional configuration props passed down to the underlying `CodeBlock` component instance. |
| `defaultColor` | `boolean` | `false` | Explicitly configured when invoking `highlight` inside `ServerCodeBlock`. |

Sources: [packages/base-ui/src/components/codeblock.rsc.tsx:5-18](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/codeblock.rsc.tsx#L5-L18), [packages/radix-ui/src/components/codeblock.rsc.tsx:5-18](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/codeblock.rsc.tsx#L5-L18)

## Dynamic Client-Side Syntax Highlighting

### Core Implementation and Dynamic Execution

The client-side syntax highlighting system is powered by the `DynamicCodeBlock` component defined in `dynamic-codeblock.core.tsx` and wrapped for specific UI packages in `dynamic-codeblock.tsx`. It relies on `useShikiDynamic` from `fumadocs-core/highlight/shiki/react` to execute Shiki on the client on-demand, accepting a `highlighter` instance or factory function, a target language (`lang`), and source `code`.

```tsx
export interface DynamicCodeblockProps {
  highlighter: HighlighterCore | (() => HighlighterCore | PromiseLike<HighlighterCore>);
  lang: string;
  code: string;
  /**
   * Extra props for the underlying `<CodeBlock />` component.
   *
   * Ignored if you defined your own `pre` component in `options.components`.
   */
  codeblock?: CodeBlockProps;
  /**
   * Wrap in React `<Suspense />` and provide a fallback.
   *
   * @defaultValue true
   */
  wrapInSuspense?: boolean;
  options: DistributiveOmit<UseShikiOptions, 'lang'>;
}
```

Sources: [packages/base-ui/src/components/dynamic-codeblock.core.tsx:8-25](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/dynamic-codeblock.core.tsx#L8-L25), [packages/radix-ui/src/components/dynamic-codeblock.core.tsx:8-25](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/dynamic-codeblock.core.tsx#L8-L25)

The component merges user-supplied options with defaults, setting `defaultColor` to `false` and injecting a `DefaultPre` component into `options.components`. `DefaultPre` uses `PropsContext` to pass extra properties down to the underlying `CodeBlock` component while maintaining zero top margins via class name concatenation with `cn`.

```tsx
function DefaultPre(props: ComponentProps<'pre'>) {
  const extraProps = use(PropsContext);

  return (
    <CodeBlock
      {...props}
      {...extraProps}
      className={cn('my-0', props.className, extraProps?.className)}
    >
      <Pre>{props.children}</Pre>
    </CodeBlock>
  );
}
```

Sources: [packages/base-ui/src/components/dynamic-codeblock.core.tsx:31-43](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/dynamic-codeblock.core.tsx#L31-L43), [packages/radix-ui/src/components/dynamic-codeblock.core.tsx:31-43](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/dynamic-codeblock.core.tsx#L31-L43)

> [!WARNING]
> Defining a custom `pre` component inside `options.components` overrides `DefaultPre`, which disables the propagation of `codeblock` properties through `PropsContext`.

Sources: [packages/base-ui/src/components/dynamic-codeblock.core.tsx:12-17](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/dynamic-codeblock.core.tsx#L12-L17), [packages/radix-ui/src/components/dynamic-codeblock.core.tsx:12-17](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/dynamic-codeblock.core.tsx#L12-L17)

### Placeholder Lifecycle and Bundles

When highlighting is pending or asynchronous, `wrapInSuspense` controls whether the component falls back to a structural placeholder. If `wrapInSuspense` is enabled and the Shiki node evaluates to null or undefined, the component renders a `Placeholder` element that splits the raw code string by newline characters and wraps each line in a `span` with the `line` class name.

```tsx
function Placeholder({
  code,
  components = {},
}: {
  code: string;
  components: UseShikiOptions['components'];
}) {
  const { pre: Pre = 'pre', code: Code = 'code' } = components as Record<string, FC>;

  return (
    <Pre>
      <Code>
        {code.split('\n').map((line, i) => (
          <span key={i} className="line">
            {line}
          </span>
        ))}
      </Code>
    </Pre>
  );
```

Sources: [packages/base-ui/src/components/dynamic-codeblock.core.tsx:69-88](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/dynamic-codeblock.core.tsx#L69-L88), [packages/radix-ui/src/components/dynamic-codeblock.core.tsx:69-88](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/dynamic-codeblock.core.tsx#L69-L88)

Package-specific bundles configure default highlighters and color themes out of the box. Both `packages/base-ui/src/components/dynamic-codeblock.tsx` and `packages/radix-ui/src/components/dynamic-codeblock.tsx` supply `defaultShikiFactory.getOrInit()` as the highlighter callback and default light and dark theme mappings to `github-light` and `github-dark`.

```tsx
export function DynamicCodeBlock(props: DynamicCodeblockProps) {
  return (
    <Base.DynamicCodeBlock
      highlighter={() => defaultShikiFactory.getOrInit()}
      options={{ themes: { light: 'github-light', dark: 'github-dark' } }}
      {...props}
    />
  );
}
```

Sources: [packages/base-ui/src/components/dynamic-codeblock.tsx:9-17](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/dynamic-codeblock.tsx#L9-L17), [packages/radix-ui/src/components/dynamic-codeblock.tsx:9-17](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/dynamic-codeblock.tsx#L9-L17)

### Dynamic Code Block Properties

| Property | Type | Default Value | Purpose |
| :--- | :--- | :--- | :--- |
| `highlighter` | `HighlighterCore \| (() => HighlighterCore \| PromiseLike<HighlighterCore>)` | *Required* | Shiki highlighter instance or a factory function returning or resolving to one. |
| `lang` | `string` | *Required* | Language identifier used for syntax parsing and highlighting rules. |
| `code` | `string` | *Required* | Raw source code string to be highlighted or rendered inside placeholders. |
| `codeblock` | `CodeBlockProps` | `undefined` | Extra properties passed to the underlying `<CodeBlock />` component wrapper. |
| `wrapInSuspense` | `boolean` | `true` | Determines whether to wrap the node and display a line-split fallback placeholder while loading. |
| `options` | `DistributiveOmit<UseShikiOptions, 'lang'>` | *Required* | Shiki configuration options excluding the language specifier. |

Sources: [packages/base-ui/src/components/dynamic-codeblock.core.tsx:8-25](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/base-ui/src/components/dynamic-codeblock.core.tsx#L8-L25), [packages/radix-ui/src/components/dynamic-codeblock.core.tsx:8-25](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/radix-ui/src/components/dynamic-codeblock.core.tsx#L8-L25)

## Markdown Transformation and Tab Grouping

### AST Transformation and Attribute Parsing

The `remarkCodeTab` unified transformer processes Markdown syntax trees to convert adjacent fenced code blocks carrying specific attributes into tabbed component structures. It inspects nodes containing child elements, skipping any node marked with `_code_tab_visited`. When visiting container elements or MDX JSX flow elements whose names match registered tab handlers, it groups consecutive code blocks that contain a `tab` attribute extracted via `parseCodeBlockAttributes`. 

```typescript
export function remarkCodeTab(
  this: Processor,
  options: RemarkCodeTabOptions = {},
): Transformer<Root, Root> {
  const { parseMdx = false, Tabs = 'CodeBlockTabs' } = options;

  return (tree) => {
    visit(tree, (node) => {
      if (!('children' in node) || node.data?._code_tab_visited) return 'skip';
      let localTabs: TabType = Tabs;
      let localParseMdx = parseMdx;
      let withParent = true;

      if (node.type === 'mdxJsxFlowElement' && node.name && node.name in Types) {
        withParent = false;
        localTabs = node.name as TabType;

        // for `Tabs` in simple mode, it doesn't support MDX tab names
        if (node.name === 'Tabs' && localParseMdx) {
          localParseMdx = node.attributes.every(
            (attribute) => attribute.type !== 'mdxJsxAttribute' || attribute.name !== 'items',
          );
        }
      }

      let start = -1;
      let end = 0;
      const close = () => {
        if (start === -1 || start === end) return;
        const replacement = Types[localTabs](
          this,
          node.children.slice(start, end) as Code[],
          localParseMdx,
          withParent,
        );

        for (const element of replacement) {
          element.data ??= {};
          element.data._code_tab_visited = true;
        }

        node.children.splice(start, end - start, ...replacement);
        end = start;
        start = -1;
      };

      for (; end < node.children.length; end++) {
        const child = node.children[end];
        if (child.type !== 'code' || !child.meta) {
          close();
          continue;
        }

        const meta = parseCodeBlockAttributes(child.meta, ['tab', 'tab-group']);
        if (!meta.attributes.tab) {
          close();
          continue;
        }

        if (start === -1) start = end;
        child.meta = meta.rest;
        child.data ??= {};
        child.data.tab = meta.attributes.tab;
        if (meta.attributes['tab-group']) {
          child.data.tabGroup = meta.attributes['tab-group'];
        }
      }

      close();
    });
  };
}
```

Sources: [packages/core/src/mdx-plugins/remark-code-tab.ts:202-273](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/remark-code-tab.ts#L202-L273)

The utility function `parseCodeBlockAttributes` uses a regular expression `AttributeRegex` to scan meta strings for space-separated key-value pairs matching attribute names, supporting both double-quoted and single-quoted values as well as boolean flags. It accepts an optional list of allowed attribute names and returns an object containing the parsed attributes dictionary and the remaining unparsed meta string.

```typescript
export interface CodeBlockAttributes<Name extends string = string> {
  attributes: Partial<Record<Name, string | null>>;
  rest: string;
}

const AttributeRegex = /(?<=^|\s)(?<name>[a-zA-Z0-9_-]+)(?:=(?:"([^"]*)"|'([^']*)'))?/g;

/**
 * Parse Fumadocs-style code block attributes from meta string, like `title="hello world"`
 */
export function parseCodeBlockAttributes<Name extends string = string>(
  meta: string,
  allowedNames?: Name[],
): CodeBlockAttributes<Name> {
  const attributes: CodeBlockAttributes['attributes'] = {};
  const rest = meta.replaceAll(AttributeRegex, (match, name, value_1, value_2) => {
    if (allowedNames && !allowedNames.includes(name)) return match;

    attributes[name] = value_1 ?? value_2 ?? null;
    return '';
  });

  return {
    rest,
    attributes,
  };
}
```

Sources: [packages/core/src/mdx-plugins/codeblock-utils.ts:89-115](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/codeblock-utils.ts#L89-L115)

> [!WARNING]
> Using the legacy `tab` meta string parameter inside Shiki transformers triggers a console warning instructing authors to migrate to the `remarkCodeTab` remark plugin instead.

Sources: [packages/core/src/mdx-plugins/rehype-code.core.ts:144-171](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/rehype-code.core.ts#L144-L171)

### Tab Handler Strategies and Group Generation

The plugin supports two distinct tab generation strategies defined in the `Types` record: `CodeBlockTabs` and `Tabs`. The `CodeBlockTabs` strategy collects grouped code blocks and delegates to `generateCodeBlockTabs` to construct an MDX JSX flow element representing optimized tab wrappers.

```typescript
const Types: Record<TabType, TabHandler> = {
  CodeBlockTabs(processor, nodes, withMdx, withParent) {
    const tabs = processTabValue(nodes);
    let isFirstTab = true;
    const options: CodeBlockTabsOptions = {
      triggers: [],
      tabs: [],
    };

    for (const [value, list] of tabs) {
      if (isFirstTab) {
        const tagGroup = list[0].data?.tabGroup;
        options.defaultValue = value;

        if (tagGroup) options.persist = { id: tagGroup };
        isFirstTab = false;
      }

      options.triggers.push({
        value,
        children: withMdx
          ? (mdxToAst(processor, value).children as BlockContent[])
          : [
              {
                type: 'text',
                value,
              },
            ],
      });
      options.tabs.push({
        value,
        children: list,
      });
    }

    const node = generateCodeBlockTabs(options);
    if (!withParent) return node.children;
    return [node];
  },
```

Sources: [packages/core/src/mdx-plugins/remark-code-tab.ts:50-88](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/remark-code-tab.ts#L50-L88)

The `generateCodeBlockTabs` builder accepts configuration options to assemble tab lists, triggers, and tab panels with optional state persistence and default values.

```typescript
export interface CodeBlockTabsOptions {
  attributes?: MdxJsxAttribute[];
  defaultValue?: string;
  persist?:
    | {
        id: string;
      }
    | false;
  triggers: {
    value: string;
    children: (BlockContent | Text)[];
  }[];
  tabs: {
    value: string;
    children: BlockContent[];
  }[];
}
```

Sources: [packages/core/src/mdx-plugins/codeblock-utils.ts:4-20](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/codeblock-utils.ts#L4-L20)

| Option Property | Type | Purpose |
| :--- | :--- | :--- |
| `attributes` | `MdxJsxAttribute[]` | Additional JSX attributes applied to the root element. |
| `defaultValue` | `string` | The initial active tab value selected on render. |
| `persist` | `{ id: string } \| false` | Configures client-side selection persistence using the specified group identifier. |
| `triggers` | `{ value: string; children: (BlockContent \| Text)[] }[]` | List of trigger buttons rendered in the tab navigation header. |
| `tabs` | `{ value: string; children: BlockContent[] }[]` | List of tab content panels containing the grouped code blocks. |

Sources: [packages/core/src/mdx-plugins/codeblock-utils.ts:4-20](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/core/src/mdx-plugins/codeblock-utils.ts#L4-L20)

## Specialized Protocol Code Block Integration

### AsyncAPI Message Examples and Payload Tabs

The `MessageExamples` component inspects asynchronous message definitions to generate protocol schema examples and test payloads. It utilizes `getExampleMessages` to extract relevant items, filtering for entries containing `payload`, `headers`, or `description`.

```tsx
const items = useMemo(
  () =>
    getExampleMessages({ message }).filter(
      (item) => item.payload !== undefined || item.headers !== undefined || item.description,
    ),
  [message],
);
```

Sources: [packages/asyncapi/src/ui/operation/message-examples.tsx:28-34](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/ui/operation/message-examples.tsx#L28-L34)

When multiple message examples exist, `MessageExamples` renders a selectable tab group using `SelectTabs` and `SelectTabTrigger`. Each individual example content block splits headers and payloads into separate interactive code blocks via `MessageExampleContent`.

```tsx
const items: { label: string; value: string; content: ReactNode }[] = [];
if (headers)
  items.push({
    label: t('Headers'),
    value: 'headers',
    content: <ClientCodeBlock lang="json" code={headers} />,
  });
if (payload)
  items.push({
    label: t('Payload'),
    value: 'payload',
    content: <ClientCodeBlock lang="json" code={payload} />,
  });

```

Sources: [packages/asyncapi/src/ui/operation/message-examples.tsx:87-100](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/ui/operation/message-examples.tsx#L87-L100)

These content blocks wrap JSON-serialized headers and payloads inside `CodeBlockTabs`, utilizing `ClientCodeBlock` components with the language set to `json`.

```tsx
<CodeBlockTabs defaultValue={items[0].value}>
  <CodeBlockTabsList>
    {items.map((item) => (
      <CodeBlockTabsTrigger key={item.value} value={item.value}>
        {item.label}
      </CodeBlockTabsTrigger>
    ))}
  </CodeBlockTabsList>
  {items.map((item) => (
    <CodeBlockTab key={item.value} value={item.value}>
      {item.content}
    </CodeBlockTab>
  ))}
</CodeBlockTabs>
```

Sources: [packages/asyncapi/src/ui/operation/message-examples.tsx:103-116](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/ui/operation/message-examples.tsx#L103-L116)

> [!NOTE]
> `ClientCodeBlock` in AsyncAPI pages first checks for a user-supplied custom `CodeBlock` component in the render context, falling back to `DynamicCodeBlock` with Shiki initialization if none is provided.

Sources: [packages/asyncapi/src/ui/components/codeblock.tsx:8-14](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/asyncapi/src/ui/components/codeblock.tsx#L8-L14)

### Python Package Component Code Highlighting

The Python documentation package defines structured UI components such as `PyFunction`, `PyAttribute`, and `PyParameter` that integrate inline code formatting and syntax highlighting via `InlineCode`.

```tsx
export function PyFunction(props: { name: string; type: string; children?: ReactNode }) {
  return (
    <figure className={cn(cardVariants())}>
      <div className="flex gap-2 items-center font-mono flex-wrap mb-4">
        <code className={cn(badgeVariants({ color: 'func' }))}>func</code>
        {props.name}
        <InlineCode
          lang="python"
          className="not-prose text-xs text-fd-muted-foreground"
          code={props.type}
        />
      </div>
      <div className="text-fd-muted-foreground prose-no-margin">{props.children}</div>
    </figure>
  );
}
```

Sources: [packages/python/src/components/index.tsx:25-40](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/python/src/components/index.tsx#L25-L40)

The `InlineCode` helper asynchronously invokes the core `highlight` function, targeting the `python` language and overriding the pre element tag to render lightweight styled inline spans.

```tsx
async function InlineCode({
  lang,
  code,
  ...rest
}: ComponentProps<'span'> & {
  lang: string;
  code: string;
}) {
  return highlight(code, {
    lang,
    components: {
      pre: (props) => <span {...props} {...rest} className={cn(rest.className, props.className)} />,
    },
  });
}
```

Sources: [packages/python/src/components/index.tsx:135-149](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/python/src/components/index.tsx#L135-L149)

| Component Name | Primary Badge Variant | Highlight Language | Purpose |
| :--- | :--- | :--- | :--- |
| `PyFunction` | `func` | `python` | Renders Python function signatures with return types and descriptions. |
| `PyAttribute` | `attribute` | `python` | Renders class or module attributes with optional types and default values. |
| `PyParameter` | `primary` | `python` | Renders parameter definitions inside function documentation cards. |
| `PyFunctionReturn` | *None* | `python` | Displays function return type annotations inside styled containers. |

Sources: [packages/python/src/components/index.tsx:14-133](https://github.com/blade47/fumadocs/blob/a9f988897e7dc4ef3d62b2865d80da2f5607701e/packages/python/src/components/index.tsx#L14-L133)

## Related

- [Syntax Highlighting](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/technical/core-architecture/syntax-highlighting)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/e8872a72-1909-479c-b585-dc74c6e26726/llms.txt) for all pages in this wiki.
