Code Block (Dynamic)
A codeblock that also highlights code. Based on Shiki 式 syntax highlighter
Made by malinskibeniaminInstallation
Usage
The Dynamic Code Block component provides server-side syntax highlighting using Shiki, with better performance and more accurate highlighting than client-side solutions:
import {
CodeBlock,
Pre,
DynamicCodeBlock
} from '@/components/redpanda-ui/code-block-dynamic';
// Basic usage with automatic syntax highlighting
const sampleCode = `function greetUser(name: string): string {
return \`Hello, \${name}! Welcome to Redpanda UI.\`;
}`;
export function Example() {
return (
<DynamicCodeBlock
lang="typescript"
code={sampleCode}
options={{
title: "greet.ts",
icon: "📄",
allowCopy: true,
theme: { light: "github-light", dark: "github-dark" }
}}
/>
);
}
// Advanced usage with custom theming and line numbers
export function AdvancedExample() {
return (
<DynamicCodeBlock
lang="typescript"
code={sampleCode}
options={{
title: "greet.ts",
icon: "📄",
allowCopy: true,
keepBackground: true,
'data-line-numbers': true,
'data-line-numbers-start': 1,
themes: {
light: "catppuccin-latte",
dark: "catppuccin-mocha"
},
components: {
// Custom component overrides
pre: (props) => <Pre className="custom-pre" {...props} />
}
}}
/>
);
}
// Manual CodeBlock usage for custom highlighting
export function ManualExample() {
return (
<CodeBlock
title="config.json"
icon="⚙️"
allowCopy={true}
keepBackground={false}
data-line-numbers={true}
>
<Pre>
<code className="language-json">
{JSON.stringify({ theme: "dark", fontSize: 14 }, null, 2)}
</code>
</Pre>
</CodeBlock>
);
}Synchronous highlighting (SyncCodeBlock)
DynamicCodeBlock tokenizes in a client effect via useShiki, so it renders a plain-text skeleton first and then swaps to highlighted markup — a brief flash that is especially visible on dark backgrounds. SyncCodeBlock avoids this by highlighting with a pre-bundled Shiki core highlighter (JS regex engine + statically imported grammars). Highlighting runs during render, so there is no loading skeleton and no highlight flash:
import { SyncCodeBlock } from '@/components/code-block-dynamic';
// Flash-free dark terminal: keepBackground keeps the theme's own background
// instead of the registry's bg-card, and both theme slots default to github-dark.
export function Terminal() {
return <SyncCodeBlock code="rpk topic create my-topic" lang="bash" keepBackground />;
}
// It accepts the same chrome props as CodeBlock (title, icon, allowCopy, ...).
export function ConfigSnippet() {
return (
<SyncCodeBlock
lang="yaml"
code={'redpanda:\n developer_mode: true'}
title="redpanda.yaml"
keepBackground
/>
);
}The trade-off is that the supported languages are fixed at build time — bash, go, java, javascript, json, python, sql, tsx, typescript, and yaml — and ship in the bundle of any module that imports SyncCodeBlock. An unsupported lang falls back to plain (unhighlighted) text rather than throwing. The grammar imports are side-effect-free, so consumers that only use DynamicCodeBlock tree-shake them away.
- Use
SyncCodeBlockwhen: you render a small, known set of languages (terminals, config/SQL snippets) and want zero flash — ideal for dark themes. - Use
DynamicCodeBlockwhen: you need arbitrary or à-la-carte languages, or line numbers and the fulloptionssurface.
When to use
Use this decision tree to determine when to use the Dynamic Code Block component over the regular Code Block:
Use Cases
- Documentation sites: Technical documentation with accurate syntax highlighting
- Code tutorials: Step-by-step programming guides with proper code display
- API references: Code examples with language-specific highlighting
- Blog posts: Technical articles with multiple code examples
- Code showcases: Portfolio or demonstration code with professional presentation
Dynamic Code Block vs Regular Code Block
- Use Dynamic Code Block when: You need server-side syntax highlighting, accurate language support, custom themes, or better performance with many code blocks
- Use regular Code Block when: Client-side highlighting is sufficient, you have simple highlighting needs, or you're not using SSR
- Use inline
<code>when: Short code snippets within text content - Use Code Editor when: Users need to edit or interact with the code
Anatomy
The Dynamic Code Block component extends the regular Code Block with Shiki-powered syntax highlighting:
Dynamic Code Block Architecture:
┌─────────────────────────────────────────────────┐
│ DynamicCodeBlock (Wrapper Component) │
│ ┌─────────────────────────────────────────────┐ │
│ │ useShiki Hook │ │
│ │ - Server-side syntax highlighting │ │
│ │ - Theme management (light/dark) │ │
│ │ - Language detection and parsing │ │
│ │ - Component transformation │ │
│ └─────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────┐ │
│ │ Loading Fallback │ │
│ │ - Plain text display during SSR │ │
│ │ - Line-by-line structure preserved │ │
│ │ - Prevents layout shift │ │
│ └─────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────┐ │
│ │ CodeBlock (Enhanced Container) │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ Header (with enhanced styling) │ │ │
│ │ │ [Icon] Title Text [CopyButton] │ │ │
│ │ │ - keepBackground option │ │ │
│ │ │ - Custom icon support │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ │ ┌─────────────────────────────────────────┐ │ │
│ │ │ Viewport (Scroll Container) │ │ │
│ │ │ ┌─────────────────────────────────────┐ │ │ │
│ │ │ │ Pre (Syntax Highlighted) │ │ │ │
│ │ │ │ ┌─────────────────────────────────┐ │ │ │ │
│ │ │ │ │ Line Elements │ │ │ │ │
│ │ │ │ │ - span.line containers │ │ │ │ │
│ │ │ │ │ - Token-based highlighting │ │ │ │ │
│ │ │ │ │ - Line number support │ │ │ │ │
│ │ │ │ │ - Counter-based numbering │ │ │ │ │
│ │ │ │ └─────────────────────────────────┘ │ │ │ │
│ │ │ └─────────────────────────────────────┘ │ │ │
│ │ └─────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────┐ │
│ │ CopyButton (Enhanced) │ │
│ │ - Animated check/copy icons │ │
│ │ - Smart text extraction │ │
│ │ - Hover state management │ │
│ │ - Accessibility features │ │
│ └─────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
Shiki Integration Flow:
┌─────────────────────────────────────┐
│ Code String + Language │
│ ↓ │
│ Shiki Highlighter │
│ - Tokenizes syntax │
│ - Applies theme colors │
│ - Generates HAST tree │
│ ↓ │
│ HAST to JSX Runtime │
│ - Converts to React elements │
│ - Preserves syntax structure │
│ - Applies custom components │
│ ↓ │
│ Rendered Highlighted Code │
└─────────────────────────────────────┘Component Composition
- DynamicCodeBlock: Main wrapper orchestrating syntax highlighting
- useShiki Hook: Custom hook managing Shiki highlighting and SSR
- CodeBlock: Enhanced container with dynamic theme support
- Pre: Structured preformatted text element with line support
- CopyButton: Advanced copy functionality with visual feedback
- Theme Management: Automatic light/dark theme switching
- Line Numbers: CSS counter-based line numbering system
- Component Override: Customizable rendering via components prop
- Performance: Server-side rendering with client hydration
Examples
Default
Credits
- We use Fumadocs as inspiration for the dynamic code block component and style.
Recent changes
- patchv1.2.0Pin shipped dependency floors to the version we develop against. Registry items now declare ranges like `^5.1.9` (the actual installed version) instead of collapsing to `^5.0.0`, so consumers start on the known-tested baseline while caret semantics still allow any compatible release within the same major.#133
- minorv1.1.0Theme docs refresh, readability pass on semantic foregrounds, and consumer-facing Base UI regression fixes.#121
- minorv1.0.0Post-Base-UI polish. Public API unchanged.#116
- majorv1.0.0Migrate every Radix-based primitive to `@base-ui/react@^1.4.0` (Base UI).#114
- minorv0.3.0Add theme-provider component to the registry with documentation and tests. Includes playground type improvements (export RegistryItem, remove as-const boilerplate) and docs site dark mode border color fix.#109