<div class="container">
<h1>Hello, world!</h1>
</div>
"use client"
import { CodeBlock, createShikiAdapter } from "@chakra-ui/react"
import type { HighlighterGeneric } from "shiki"
const file = {
code: `
<div class="container">
<h1>Hello, world!</h1>
</div>
`,
language: "html",
title: "index.html",
}
const Demo = () => {
return (
<CodeBlock.AdapterProvider value={shikiAdapter}>
<CodeBlock.Root code={file.code} language={file.language}>
<CodeBlock.Content>
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
</CodeBlock.Content>
</CodeBlock.Root>
</CodeBlock.AdapterProvider>
)
}
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["tsx", "scss", "html", "bash", "json"],
themes: ["github-dark", "github-light"],
})
},
})
Usage
import { CodeBlock } from "@chakra-ui/react"
<CodeBlock.AdapterProvider>
<CodeBlock.Root>
<CodeBlock.Header>
<CodeBlock.Title />
<CodeBlock.Control>
<CodeBlock.CopyTrigger />
<CodeBlock.CollapseTrigger />
</CodeBlock.Control>
</CodeBlock.Header>
<CodeBlock.Content>
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
</CodeBlock.Content>
</CodeBlock.Root>
</CodeBlock.AdapterProvider>
Adapters
The CodeBlock component works for Shiki and Highlight.js highlighting engines.
The docs assumes Shiki by default.
To setup the code block component, you need to:
- Configure your preferred adapter (Shiki or Highlight.js).
- Provide the adapter to the
CodeBlock.AdapterProvider
at the top level. - Render the
CodeBlock.Root
component within theCodeBlock.AdapterProvider
.
Shiki
Install the shiki
package.
npm install shiki
Then, create the shiki adapter that dynamically loads the shiki highlighter for the selected languages.
import type { HighlighterGeneric } from "shiki"
import { createShikiAdapter } from "@chakra-ui/react"
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["tsx", "json"],
themes: ["github-dark", "github-light"],
})
},
})
<CodeBlock.AdapterProvider value={shikiAdapter}>
{/* ... */}
</CodeBlock.AdapterProvider>
Highlight.js
Install the highlight.js
package.
npm install highlight.js
Then, create the highlight.js adapter that dynamically loads the selected languages.
import { createHighlightJsAdapter } from "@chakra-ui/react"
import hljs from "highlight.js/lib/core"
const highlightJsAdapter = createHighlightJsAdapter<typeof hljs>({
async load() {
const languages = {
tsx: () => import("highlight.js/lib/languages/typescript"),
html: () => import("highlight.js/lib/languages/xml"),
}
await Promise.all(
Object.entries(languages).map(async ([language, file]) => {
const { default: langModule } = await file()
hljs.registerLanguage(language, langModule)
}),
)
return hljs
},
})
Examples
Sizes
Use the size
prop to change the size of the code block component.
<div class="container">
<h1>Hello, world!</h1>
</div>
<div class="container">
<h1>Hello, world!</h1>
</div>
<div class="container">
<h1>Hello, world!</h1>
</div>
"use client"
import { CodeBlock, For, Stack, createShikiAdapter } from "@chakra-ui/react"
import type { HighlighterGeneric } from "shiki"
const file = {
code: `
<div class="container">
<h1>Hello, world!</h1>
</div>
`,
language: "html",
title: "index.html",
}
const Demo = () => {
return (
<CodeBlock.AdapterProvider value={shikiAdapter}>
<Stack gap="8">
<For each={["sm", "md", "lg"]}>
{(size) => (
<CodeBlock.Root
key={size}
code={file.code}
language={file.language}
size={size}
>
<CodeBlock.Header>
<CodeBlock.Title>(size={size})</CodeBlock.Title>
</CodeBlock.Header>
<CodeBlock.Content>
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
</CodeBlock.Content>
</CodeBlock.Root>
)}
</For>
</Stack>
</CodeBlock.AdapterProvider>
)
}
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["tsx", "scss", "html", "bash", "json"],
themes: ["github-dark", "github-light"],
})
},
})
Title
Render the CodeBlock.Title
component within the CodeBlock.Header
component
to add a title to the code block component.
<div class="container">
<h1>Hello, world!</h1>
</div>
"use client"
import { CodeBlock, Icon, createShikiAdapter } from "@chakra-ui/react"
import { FaHtml5 } from "react-icons/fa"
import type { HighlighterGeneric } from "shiki"
const file = {
code: `
<div class="container">
<h1>Hello, world!</h1>
</div>
`,
language: "html",
title: "index.html",
}
const Demo = () => {
return (
<CodeBlock.AdapterProvider value={shikiAdapter}>
<CodeBlock.Root code={file.code} language={file.language}>
<CodeBlock.Header>
<CodeBlock.Title>
<Icon as={FaHtml5} color="orange.300" />
{file.title}
</CodeBlock.Title>
</CodeBlock.Header>
<CodeBlock.Content>
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
</CodeBlock.Content>
</CodeBlock.Root>
</CodeBlock.AdapterProvider>
)
}
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["tsx", "scss", "html", "bash", "json"],
themes: ["github-dark", "github-light"],
})
},
})
Copy button
Use the copyButton
prop to add a copy button to the code block component.
<div class="container">
<h1>Hello, world!</h1>
</div>
"use client"
import { CodeBlock, IconButton, createShikiAdapter } from "@chakra-ui/react"
import type { HighlighterGeneric } from "shiki"
const file = {
code: `
<div class="container">
<h1>Hello, world!</h1>
</div>
`,
language: "html",
title: "index.html",
}
const Demo = () => {
return (
<CodeBlock.AdapterProvider value={shikiAdapter}>
<CodeBlock.Root code={file.code} language={file.language}>
<CodeBlock.Header>
<CodeBlock.Title>{file.title}</CodeBlock.Title>
<CodeBlock.CopyTrigger asChild>
<IconButton variant="ghost" size="2xs">
<CodeBlock.CopyIndicator />
</IconButton>
</CodeBlock.CopyTrigger>
</CodeBlock.Header>
<CodeBlock.Content>
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
</CodeBlock.Content>
</CodeBlock.Root>
</CodeBlock.AdapterProvider>
)
}
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["tsx", "scss", "html", "bash", "json"],
themes: ["github-dark"],
})
},
})
Line numbers
Line numbers make it easier to reference specific lines of code. Pass the
meta.showLineNumbers
prop to show line numbers in the code block component.
<div class="container">
<h1>Hello, world!</h1>
</div>
"use client"
import { CodeBlock, createShikiAdapter } from "@chakra-ui/react"
import type { HighlighterGeneric } from "shiki"
const file = {
code: `
<div class="container">
<h1>Hello, world!</h1>
</div>
`,
language: "html",
title: "index.html",
}
const Demo = () => {
return (
<CodeBlock.AdapterProvider value={shikiAdapter}>
<CodeBlock.Root
code={file.code}
language={file.language}
meta={{ showLineNumbers: true }}
>
<CodeBlock.Content>
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
</CodeBlock.Content>
</CodeBlock.Root>
</CodeBlock.AdapterProvider>
)
}
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["tsx", "scss", "html", "bash", "json"],
themes: ["github-dark", "github-light"],
})
},
})
Line highlighting
Pass the meta.highlightLines
prop to the CodeBlock.Root
component to
highlight specific lines of code. The prop accepts an array of line numbers.
<div class="container">
<h1>Hello, world!</h1>
</div>
"use client"
import { CodeBlock, createShikiAdapter } from "@chakra-ui/react"
import type { HighlighterGeneric } from "shiki"
const file = {
code: `
<div class="container">
<h1>Hello, world!</h1>
</div>
`,
language: "html",
title: "index.html",
}
const Demo = () => {
return (
<CodeBlock.AdapterProvider value={shikiAdapter}>
<CodeBlock.Root
code={file.code}
language={file.language}
meta={{ highlightLines: [2, 1] }}
>
<CodeBlock.Content>
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
</CodeBlock.Content>
</CodeBlock.Root>
</CodeBlock.AdapterProvider>
)
}
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["tsx", "scss", "html", "bash", "json"],
themes: ["github-dark", "github-light"],
})
},
})
Line focus
Pass the meta.highlightLines
prop to the CodeBlock.Root
component to
highlight specific lines of code. The prop accepts an array of line numbers. The
line numbers are 1-based.
const greeting = "Hello, World!"
function sayHello() {
console.log(greeting);
}
sayHello()
"use client"
import { CodeBlock, createShikiAdapter } from "@chakra-ui/react"
import type { HighlighterGeneric } from "shiki"
const file = {
code: `
const greeting = "Hello, World!"
function sayHello() {
console.log(greeting);
}
sayHello()
`,
language: "tsx",
title: "index.tsx",
}
const Demo = () => {
return (
<CodeBlock.AdapterProvider value={shikiAdapter}>
<CodeBlock.Root
code={file.code}
language={file.language}
meta={{ focusedLineNumbers: [3, 7] }}
>
<CodeBlock.Content>
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
</CodeBlock.Content>
</CodeBlock.Root>
</CodeBlock.AdapterProvider>
)
}
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["tsx", "scss", "html", "bash", "json"],
themes: ["github-dark", "github-light"],
})
},
})
Diff
Diffs are useful for highlighting source code changes. Use the
meta.addedLineNumbers
and meta.removedLineNumbers
props to add line numbers
to the code block component.
The prop accepts an array of line numbers. The line numbers are 1-based.
const greeting = "Hello, World!";
function sayHello() {
console.log("Hello, World!");
console.log(greeting);
}
sayHello();
"use client"
import { CodeBlock, createShikiAdapter } from "@chakra-ui/react"
import type { HighlighterGeneric } from "shiki"
const file = {
code: `
const greeting = "Hello, World!";
function sayHello() {
console.log("Hello, World!");
console.log(greeting);
}
sayHello();
`,
language: "tsx",
title: "index.tsx",
}
const Demo = () => {
return (
<CodeBlock.AdapterProvider value={shikiAdapter}>
<CodeBlock.Root
code={file.code}
language={file.language}
meta={{
showLineNumbers: true,
addedLineNumbers: [1, 4],
removedLineNumbers: [3],
}}
>
<CodeBlock.Content>
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
</CodeBlock.Content>
</CodeBlock.Root>
</CodeBlock.AdapterProvider>
)
}
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["tsx", "scss", "html", "bash", "json"],
themes: ["github-dark", "github-light"],
})
},
})
Max lines
Use the meta.maxLines
prop to limit the number of lines in the code block
component. By default, the code block component will expand to fit the content.
import * as React from 'react';
import { CodeBlock } from '@chakra-ui/react';
const Example = () => {
const code = `
{
"name": "My App",
"version": "1.0.0",
"description": "A simple web application",
"main": "index.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"build": "webpack --mode production",
"test": "jest"
},
"dependencies": {
"express": "^4.18.2",
"react": "^18.2.0",
"axios": "^1.4.0"
},
"author": "Developer",
"license": "MIT"
}
`
return (
<CodeBlock.Root language="json" code={code}>
<CodeBlock.Header>
<CodeBlock.Title>{file.title}</CodeBlock.Title>
</CodeBlock.Header>
</CodeBlock.Root>
);
};
export default Example;
"use client"
import { CodeBlock, IconButton, createShikiAdapter } from "@chakra-ui/react"
import type { HighlighterGeneric } from "shiki"
const Demo = () => {
return (
<CodeBlock.AdapterProvider value={shikiAdapter}>
<CodeBlock.Root code={file.code} language={file.language} maxLines={10}>
<CodeBlock.Header>
<CodeBlock.Title>{file.title}</CodeBlock.Title>
<CodeBlock.Control>
<CodeBlock.CollapseTrigger asChild>
<IconButton variant="ghost" size="2xs">
<CodeBlock.CollapseIndicator />
</IconButton>
</CodeBlock.CollapseTrigger>
<CodeBlock.CopyTrigger asChild>
<IconButton variant="ghost" size="2xs">
<CodeBlock.CopyIndicator />
</IconButton>
</CodeBlock.CopyTrigger>
</CodeBlock.Control>
</CodeBlock.Header>
<CodeBlock.Content>
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
<CodeBlock.Overlay>
<CodeBlock.CollapseTrigger>
<CodeBlock.CollapseText textStyle="sm" />
</CodeBlock.CollapseTrigger>
</CodeBlock.Overlay>
</CodeBlock.Content>
</CodeBlock.Root>
</CodeBlock.AdapterProvider>
)
}
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["tsx", "scss", "html", "bash", "json"],
themes: ["github-dark", "github-light"],
})
},
})
const file = {
code: `import * as React from 'react';
import { CodeBlock } from '@chakra-ui/react';
const Example = () => {
const code = \`
{
"name": "My App",
"version": "1.0.0",
"description": "A simple web application",
"main": "index.js",
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js",
"build": "webpack --mode production",
"test": "jest"
},
"dependencies": {
"express": "^4.18.2",
"react": "^18.2.0",
"axios": "^1.4.0"
},
"author": "Developer",
"license": "MIT"
}
\`
return (
<CodeBlock.Root language="json" code={code}>
<CodeBlock.Header>
<CodeBlock.Title>{file.title}</CodeBlock.Title>
</CodeBlock.Header>
</CodeBlock.Root>
);
};
export default Example;
`,
language: "tsx",
title: "index.tsx",
}
Language switcher
Here's an example that re-creates an API endpoint request component by composing
the CodeBlock
and Select
components.
from github import Github
# Create a Github instance using an access token
g = Github("YOUR_ACCESS_TOKEN")
# Get a repository
repo = g.get_repo("octocat/Hello-World")
# Get repository information
print(f"Repository: {repo.name}")
print(f"Description: {repo.description}")
print(f"Stars: {repo.stargazers_count}")
# List issues
issues = repo.get_issues(state='open')
for issue in issues:
print(f"Issue #{issue.number}: {issue.title}")
"use client"
import {
Badge,
CodeBlock,
HStack,
Icon,
IconButton,
Select,
Span,
createListCollection,
createShikiAdapter,
useSelect,
} from "@chakra-ui/react"
import { IoLogoJavascript, IoLogoPython } from "react-icons/io5"
import type { HighlighterGeneric } from "shiki"
const Demo = () => {
const select = useSelect({
positioning: { strategy: "fixed" },
defaultValue: [files[0].value],
collection,
})
const selectedFile = select.selectedItems[0]
return (
<CodeBlock.AdapterProvider value={shikiAdapter}>
<CodeBlock.Root
code={selectedFile.code}
language={selectedFile.language}
size="lg"
>
<CodeBlock.Header>
<HStack flex="1">
<Badge colorPalette="teal" fontWeight="bold">
POST
</Badge>
<Span textStyle="xs">/v1/search</Span>
</HStack>
<CodeBlock.Control>
<LanguageSwitcher value={select} />
<CodeBlock.CopyTrigger asChild>
<IconButton variant="ghost" size="2xs">
<CodeBlock.CopyIndicator />
</IconButton>
</CodeBlock.CopyTrigger>
</CodeBlock.Control>
</CodeBlock.Header>
<CodeBlock.Content>
<CodeBlock.Code fontSize="xs">
<CodeBlock.CodeText />
</CodeBlock.Code>
</CodeBlock.Content>
</CodeBlock.Root>
</CodeBlock.AdapterProvider>
)
}
function LanguageSwitcher(props: Select.RootProviderProps) {
const { value: select } = props
return (
<Select.RootProvider size="xs" variant="subtle" {...props}>
<Select.Control>
<Select.Trigger>
<Select.ValueText />
<Select.Indicator />
</Select.Trigger>
</Select.Control>
<Select.Positioner>
<Select.Content>
{select.collection.items.map((item) => (
<Select.Item item={item} key={item.value}>
{item.icon}
<Select.ItemText>{item.value}</Select.ItemText>
</Select.Item>
))}
</Select.Content>
</Select.Positioner>
</Select.RootProvider>
)
}
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["python", "typescript"],
themes: ["github-dark", "github-light"],
})
},
})
interface CodeFile {
value: string
code: string
language: string
title: string
icon: React.ReactElement
}
const files: CodeFile[] = [
{
value: "python",
code: `
from github import Github
# Create a Github instance using an access token
g = Github("YOUR_ACCESS_TOKEN")
# Get a repository
repo = g.get_repo("octocat/Hello-World")
# Get repository information
print(f"Repository: {repo.name}")
print(f"Description: {repo.description}")
print(f"Stars: {repo.stargazers_count}")
# List issues
issues = repo.get_issues(state='open')
for issue in issues:
print(f"Issue #{issue.number}: {issue.title}")
`,
language: "python",
title: "python.py",
icon: <Icon as={IoLogoPython} size="xs" color="orange.500" />,
},
{
value: "typescript",
code: `
import { Octokit } from "@octokit/rest";
// Create an Octokit instance
const octokit = new Octokit({
auth: "YOUR_ACCESS_TOKEN",
});
// Get repository information
const { data: repo } = await octokit.rest.repos.get({
owner: "octocat",
repo: "Hello-World",
});
console.log(\`Repository: \${repo.name}\`);
console.log(\`Description: \${repo.description}\`);
console.log(\`Stars: \${repo.stargazers_count}\`);
// List issues
const { data: issues } = await octokit.rest.issues.listForRepo({
owner: "octocat",
repo: "Hello-World",
state: "open",
});
issues.forEach((issue) => {
console.log(\`Issue #\${issue.number}: \${issue.title}\`);
});
`,
language: "typescript",
title: "typescript.ts",
icon: <Icon as={IoLogoJavascript} size="xs" color="blue.500" />,
},
]
const collection = createListCollection({
items: files,
itemToString: (item) => item.value,
itemToValue: (item) => item.value,
})
Floating copy button
Use the meta.floatingCopyButton
prop to add a floating copy button to the code
block component.
<div class="container">
<h1>Hello, world!</h1>
</div>
"use client"
import {
CodeBlock,
Float,
IconButton,
createShikiAdapter,
} from "@chakra-ui/react"
import type { HighlighterGeneric } from "shiki"
const file = {
code: `
<div class="container">
<h1>Hello, world!</h1>
</div>
`,
language: "html",
title: "index.html",
}
const Demo = () => {
return (
<CodeBlock.AdapterProvider value={shikiAdapter}>
<CodeBlock.Root code={file.code} language={file.language}>
<CodeBlock.Content>
<Float placement="top-end" offset="5" zIndex="1">
<CodeBlock.CopyTrigger asChild>
<IconButton variant="ghost" size="2xs">
<CodeBlock.CopyIndicator />
</IconButton>
</CodeBlock.CopyTrigger>
</Float>
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
</CodeBlock.Content>
</CodeBlock.Root>
</CodeBlock.AdapterProvider>
)
}
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["tsx", "scss", "html", "bash", "json"],
themes: ["github-dark", "github-light"],
})
},
})
Tabs
Here's an example that composes the CodeBlock
component with the Tabs
component to create a code block with tabs.
print('Hello, World!')
"use client"
import {
CodeBlock,
IconButton,
Tabs,
createShikiAdapter,
useTabs,
} from "@chakra-ui/react"
import type { HighlighterGeneric } from "shiki"
const Demo = () => {
const tabs = useTabs({
defaultValue: "python",
})
const activeTab =
files.find((file) => file.language === tabs.value) || files[0]
const otherTabs = files.filter((file) => file.language !== tabs.value)
return (
<CodeBlock.AdapterProvider value={shikiAdapter}>
<Tabs.RootProvider value={tabs} size="sm" variant="line">
<CodeBlock.Root code={activeTab.code} language={activeTab.language}>
<CodeBlock.Header borderBottomWidth="1px">
<Tabs.List w="full" border="0" ms="-1">
{files.map((file) => (
<Tabs.Trigger
colorPalette="teal"
key={file.language}
value={file.language}
textStyle="xs"
>
{file.title}
</Tabs.Trigger>
))}
</Tabs.List>
<CodeBlock.CopyTrigger asChild>
<IconButton variant="ghost" size="2xs">
<CodeBlock.CopyIndicator />
</IconButton>
</CodeBlock.CopyTrigger>
</CodeBlock.Header>
<CodeBlock.Content>
{otherTabs.map((file) => (
<Tabs.Content key={file.language} value={file.language} />
))}
<Tabs.Content pt="1" value={activeTab.language}>
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
</Tabs.Content>
</CodeBlock.Content>
</CodeBlock.Root>
</Tabs.RootProvider>
</CodeBlock.AdapterProvider>
)
}
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["python", "typescript", "java"],
themes: ["github-dark", "github-light"],
})
},
})
const files = [
{ title: "Python", language: "python", code: "print('Hello, World!')" },
{
title: "TypeScript",
language: "typescript",
code: "console.log('Hello, World!')",
},
{
title: "Java",
language: "java",
code: "System.out.println('Hello, World!');",
},
]
Tabs sync
Here's an example that automatically syncs all code blocks that share the same storage key. Useful for package manager or framework specific code blocks in a documentation site.
npm install @chakra-ui/react
npm install @chakra-ui/react
"use client"
import {
CodeBlock,
IconButton,
Stack,
Tabs,
createShikiAdapter,
useTabs,
} from "@chakra-ui/react"
import { useEffect } from "react"
import type { HighlighterGeneric } from "shiki"
const files = [
{ title: "npm", language: "bash", code: "npm install @chakra-ui/react" },
{
title: "yarn",
language: "bash",
code: "yarn add @chakra-ui/react",
},
{
title: "bun",
language: "bash",
code: "bun install @chakra-ui/react",
},
]
const Demo = () => {
return (
<CodeBlock.AdapterProvider value={shikiAdapter}>
<Stack gap="8">
<CodeTabs />
<CodeTabs />
</Stack>
</CodeBlock.AdapterProvider>
)
}
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["bash"],
themes: ["github-dark", "github-light"],
})
},
})
const CodeTabs = () => {
const tabs = useTabsSync({
defaultValue: files[0].title,
storageKey: "code-tabs-sync",
})
const activeTab = files.find((file) => file.title === tabs.value) || files[0]
const otherTabs = files.filter((file) => file.title !== tabs.value)
return (
<Tabs.RootProvider value={tabs} size="sm" variant="line">
<CodeBlock.Root code={activeTab.code} language={activeTab.language}>
<CodeBlock.Header borderBottomWidth="1px">
<Tabs.List w="full" border="0" ms="-1">
{files.map((file) => (
<Tabs.Trigger
colorPalette="teal"
key={file.title}
value={file.title}
textStyle="xs"
>
{file.title}
</Tabs.Trigger>
))}
</Tabs.List>
<CodeBlock.CopyTrigger asChild>
<IconButton variant="ghost" size="2xs">
<CodeBlock.CopyIndicator />
</IconButton>
</CodeBlock.CopyTrigger>
</CodeBlock.Header>
<CodeBlock.Content>
{otherTabs.map((file) => (
<Tabs.Content key={file.title} value={file.title} />
))}
<Tabs.Content pt="1" value={activeTab.title}>
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
</Tabs.Content>
</CodeBlock.Content>
</CodeBlock.Root>
</Tabs.RootProvider>
)
}
function useTabsSync(props: { defaultValue: string; storageKey: string }) {
const { defaultValue, storageKey } = props
const tabs = useTabs({
defaultValue,
onValueChange(details) {
if (details.value) {
localStorage.setItem(storageKey, details.value)
dispatchEvent(
new StorageEvent("storage", {
key: storageKey,
newValue: details.value,
}),
)
}
},
})
useEffect(() => {
const handleStorageChange = (e: StorageEvent) => {
requestAnimationFrame(() => {
if (e.key === storageKey && e.newValue) {
tabs.setValue(e.newValue)
}
})
}
window.addEventListener("storage", handleStorageChange)
return () => window.removeEventListener("storage", handleStorageChange)
}, [storageKey, tabs])
return tabs
}
Themes
Use the meta.colorScheme
prop to add a theme to the code block component. In
this example, the colorScheme is set to color mode from the useColorMode
hook.
"use client"
import { ClientOnly, CodeBlock, createShikiAdapter } from "@chakra-ui/react"
import { useColorMode } from "@/components/ui/color-mode"
import type { HighlighterGeneric } from "shiki"
const file = {
code: `
<div class="container">
<h1>Hello, world!</h1>
</div>
`,
language: "html",
title: "index.html",
}
const Demo = () => {
const { colorMode } = useColorMode()
return (
<CodeBlock.AdapterProvider value={shikiAdapter}>
<ClientOnly>
{() => (
<CodeBlock.Root
code={file.code}
language={file.language}
meta={{ colorScheme: colorMode }}
>
<CodeBlock.Content bg="bg">
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
</CodeBlock.Content>
</CodeBlock.Root>
)}
</ClientOnly>
</CodeBlock.AdapterProvider>
)
}
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["tsx", "scss", "html", "bash", "json"],
themes: ["github-dark", "github-light"],
})
},
})
Wrap overflow
Use the meta.wordWrap
prop to wrap the code block component.
const greeting = "Hello, World! I am a long line of text that will wrap to the next line."
function sayHello() {
console.log(greeting)
}
sayHello()
"use client"
import { CodeBlock, IconButton, createShikiAdapter } from "@chakra-ui/react"
import type { HighlighterGeneric } from "shiki"
const file = {
code: `
const greeting = "Hello, World! I am a long line of text that will wrap to the next line."
function sayHello() {
console.log(greeting)
}
sayHello()
`,
language: "tsx",
title: "index.tsx",
}
const Demo = () => {
return (
<CodeBlock.AdapterProvider value={shikiAdapter}>
<CodeBlock.Root
maxW="md"
code={file.code}
language={file.language}
meta={{ wordWrap: true }}
>
<CodeBlock.Header>
<CodeBlock.Title>{file.title}</CodeBlock.Title>
<CodeBlock.CopyTrigger asChild>
<IconButton variant="ghost" size="2xs">
<CodeBlock.CopyIndicator />
</IconButton>
</CodeBlock.CopyTrigger>
</CodeBlock.Header>
<CodeBlock.Content>
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
</CodeBlock.Content>
</CodeBlock.Root>
</CodeBlock.AdapterProvider>
)
}
const shikiAdapter = createShikiAdapter<HighlighterGeneric<any, any>>({
async load() {
const { createHighlighter } = await import("shiki")
return createHighlighter({
langs: ["tsx", "scss", "html", "bash", "json"],
themes: ["github-dark", "github-light"],
})
},
})
Highlight.js
Here's an example that uses highlight.js to highlight the code block.
<div class="container">
<h1>Hello, world!</h1>
</div>
"use client"
import { CodeBlock, createHighlightJsAdapter } from "@chakra-ui/react"
import hljs from "highlight.js/lib/core"
const file = {
code: `
<div class="container">
<h1>Hello, world!</h1>
</div>
`,
language: "html",
title: "index.html",
}
const Demo = () => {
return (
<CodeBlock.AdapterProvider value={highlightJsAdapter}>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark.min.css"
/>
<CodeBlock.Root code={file.code} language={file.language}>
<CodeBlock.Header>
<CodeBlock.Title>{file.title}</CodeBlock.Title>
</CodeBlock.Header>
<CodeBlock.Content>
<CodeBlock.Code>
<CodeBlock.CodeText />
</CodeBlock.Code>
</CodeBlock.Content>
</CodeBlock.Root>
</CodeBlock.AdapterProvider>
)
}
const highlightJsAdapter = createHighlightJsAdapter<typeof hljs>({
async load() {
const languages = {
tsx: () => import("highlight.js/lib/languages/typescript"),
html: () => import("highlight.js/lib/languages/xml"),
}
await Promise.all(
Object.entries(languages).map(async ([language, file]) => {
const { default: langModule } = await file()
hljs.registerLanguage(language, langModule)
}),
)
return hljs
},
})
Plain text
The code block falls back to a plain text by default. To create a plain text
code block, remove the use of CodeBlock.AdapterProvider
.
$npm install @chakra-ui/react
"use client"
import { CodeBlock, Float, IconButton, Span } from "@chakra-ui/react"
const file = {
code: "npm install @chakra-ui/react",
language: "bash",
title: "npm install @chakra-ui/react",
}
const Demo = () => {
return (
<CodeBlock.Root
code={file.code}
language={file.language}
display="inline-flex"
>
<CodeBlock.Content>
<Float placement="middle-end" offsetX="6" zIndex="1">
<CodeBlock.CopyTrigger asChild>
<IconButton variant="ghost" size="2xs">
<CodeBlock.CopyIndicator />
</IconButton>
</CodeBlock.CopyTrigger>
</Float>
<CodeBlock.Code pe="10">
<Span color="fg.muted" ms="4" userSelect="none">
$
</Span>
<CodeBlock.CodeText display="inline-block" />
</CodeBlock.Code>
</CodeBlock.Content>
</CodeBlock.Root>
)
}
Props
Prop | Default | Type |
---|---|---|
colorPalette | 'gray' | 'gray' | 'red' | 'orange' | 'yellow' | 'green' | 'teal' | 'blue' | 'cyan' | 'purple' | 'pink' The color palette of the component |
variant | 'subtle' | 'solid' | 'subtle' | 'outline' | 'surface' | 'plain' The variant of the component |
size | 'sm' | 'xs' | 'sm' | 'md' | 'lg' The size of the component |
as | React.ElementType The underlying element to render. | |
asChild | boolean Use the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |