Code Block
NewShows code with syntax highlighting, line numbers, diffs, tabs, and a copy button.
Preview
Installation
npx sigma-ui@latest add code-blockThe install adds shiki. Highlighting loads in the browser on first use, and each language is a separate chunk. A page with no highlighted block loads no shiki code.
Usage
CodeBlockContent renders the code. Pass code and the block shows it as plain text. To highlight it, pass the same code to useHighlightedCode and its lines to CodeBlockContent. The block shows plain text until the tokens arrive.
<script setup lang="ts">
import {
CodeBlock,
CodeBlockActions,
CodeBlockContent,
CodeBlockCopyButton,
CodeBlockHeader,
CodeBlockTitle,
useHighlightedCode,
} from '@/components/ui/code-block';
const code = `export function sum(a: number, b: number) {
return a + b;
}`;
const { lines } = useHighlightedCode(code, 'ts');
</script>
<template>
<CodeBlock>
<CodeBlockHeader>
<CodeBlockTitle>math.ts</CodeBlockTitle>
<CodeBlockActions>
<CodeBlockCopyButton />
</CodeBlockActions>
</CodeBlockHeader>
<CodeBlockContent
:code="code"
:lines="lines"
language="ts"
line-numbers
/>
</CodeBlock>
</template>CodeBlockCopyButton copies the code of the CodeBlockContent next to it. Use value to copy something else. CodeBlockWrapButton and CodeBlockExpandButton also control the block they sit in.
Line numbers and highlighted lines
highlightLines, focusLines, addedLines, and removedLines take "1,4-6" or an array of numbers. They count the lines of code from 1, whatever startLine is. startLine only changes the numbers in the gutter. To highlight what is shown as line 97 with start-line="96", pass highlight-lines="2".
When a line matches more than one prop, removed wins over added, and added wins over highlighted.
Diffs
Set diff and write the code as a diff: a leading + marks an added line, - a removed line, and a space an unchanged line. The markers are hidden and shown in the gutter instead. +++ and --- file headers are left as they are. The copy button copies the code after the change, without the removed lines.
Pass diff to useHighlightedCode too, so the tokens match the lines.
Notation comments
Set notation to read shiki notation comments at the end of a line. The comments are hidden. Pass notation to useHighlightedCode too.
| Comment | Effect |
|---|---|
// [!code highlight] or // [!code hl] | Highlights the line. |
// [!code focus] | Focuses the line and dims the others. |
// [!code ++] and // [!code --] | Marks the line as added or removed. |
// [!code word:name] | Marks name on the line. On a line of its own, it marks name on every line after it. |
Add :N to apply a comment to N lines, as in // [!code highlight:3] or // [!code word:name:2]. Use the comment syntax of the language: #, --, <!-- -->, and /* */ work too.
Languages
These languages are included: bash, css, diff, html, javascript, json, jsx, markdown, python, sql, tsx, typescript, vue, yaml, and ansi. Aliases such as ts, js, sh, shell, console, md, py, and yml work too. Any other language shows as plain text.
To add one, add it to codeBlockLanguages in highlighter.ts:
export const codeBlockLanguages: Record<string, LanguageInput> = {
// ...
rust: () => import('shiki/langs/rust.mjs'),
};The shiki core and the JavaScript regex engine are about 55 KB gzipped. Each language adds its own chunk the first time it is used: TypeScript is about 16 KB and Vue about 60 KB, because it includes the languages it embeds.
Theming
The default theme uses CSS variables, so the colors follow your theme and dark mode. Override them on [data-slot='code-block'], and for dark mode on .dark [data-slot='code-block'].
| Variable | Used for |
|---|---|
--code-block-background, --code-block-foreground | The block and its text. |
--code-block-header, --code-block-border | The header and the borders. |
--code-block-line-number | Line numbers. |
--code-block-token-* | Token colors: keyword, string, constant, function, parameter, comment, punctuation, link. |
--code-block-ansi-* | ANSI colors, such as red, green, and bright-black. |
--code-block-highlighted, --code-block-highlighted-border | Highlighted lines. |
--code-block-added, --code-block-added-border, --code-block-added-marker | Added lines. |
--code-block-removed, --code-block-removed-border, --code-block-removed-marker | Removed lines. |
--code-block-word, --code-block-word-border | Marked words. |
--code-block-destructive-foreground | Text in the destructive variant. |
To use shiki themes instead, pass a light and a dark theme to useHighlightedCode. The .dark class picks between them. The block keeps its own background, so check that the theme colors are readable on it.
const { lines } = useHighlightedCode(code, 'ts', {
themes: {
light: () => import('shiki/themes/github-light.mjs'),
dark: () => import('shiki/themes/github-dark.mjs'),
},
});Server rendering
useHighlightedCode does nothing on the server, so the server and the first client render show the same plain text. To send highlighted HTML from the server, call highlightCode while rendering, for example in Nuxt's useAsyncData, and pass the result as initial. The result is plain JSON.
const { data: initial } = await useAsyncData('example', () => highlightCode(code, 'ts'));
const { lines } = useHighlightedCode(code, 'ts', { initial: initial.value });Examples
Header with a file name
Plain text
Without lines, the code renders as plain text.
Line numbers
Start line
startLine changes the first number. highlightLines still counts from the first line of code.
Highlighted lines
Highlighted words
highlightWords takes strings and regular expressions.
Focus
Lines outside focusLines are dimmed until the pointer is over the block or focus is inside it.
Diff
Notation comments
Wrapping
CodeBlockWrapButton toggles wrapping. Use v-model:wrap on CodeBlock to set it yourself.
Maximum height
maxHeight caps the height. The content scrolls inside and can be scrolled with the keyboard.
Expand
With collapsedHeight, the block starts short. CodeBlockExpandButton shows the rest, and it only renders when the code is taller than collapsedHeight.
Files in tabs
CodeBlockTabs puts several files in one block. The actions in the header apply to the active tab.
Package managers
The choice is saved with useStorage, so it is the same in every block and after a reload.
Terminal output
The ansi language reads ANSI color codes, so terminal output keeps its colors.
Streaming
While code grows, only the new lines are highlighted again. Disable the copy button until the code is complete.
Actions without a header
Put CodeBlockActions directly in CodeBlock and the actions float in the top corner. They show on hover and on focus, and always on touch screens.
Small
size="sm" is for tool output and logs.
Destructive
Shiki themes
Custom lines
Put CodeBlockLine elements in CodeBlockContent to build the lines yourself. code is then only what the copy button copies.
API Reference
CodeBlock
Props
| Prop | Type | Default |
|---|---|---|
variantTone of the block. | "default" | "destructive" | null | "default" |
size
| "default" | "sm" | null | "default" |
wrapWhether long lines wrap instead of scrolling. Use with | boolean | - |
expandedWhether a block with | boolean | - |
Events
| Event | Payload |
|---|---|
update:wrapEmitted when the wrap button toggles wrapping. | [value: boolean] |
update:expandedEmitted when the expand button shows or hides lines. | [value: boolean] |
Slots
| Slot | Scope |
|---|---|
defaultHeader, content, and footer parts. | - |
CodeBlockActions
Slots
| Slot | Scope |
|---|---|
defaultButtons such as | - |
CodeBlockContent
Props
| Prop | Type | Default |
|---|---|---|
codeThe code to show. It is also what | string | - |
linesHighlighted tokens from | CodeToken[][] | null | - |
languageLanguage name, set as | string | - |
wrapWraps long lines instead of scrolling. Defaults to the | boolean | - |
lineNumbersNumbers each line. Numbers are not selected or copied. | boolean | false |
startLineNumber of the first line when | number | 1 |
highlightLinesLines to highlight, as | LineSelector | - |
focusLinesLines to keep in focus while the others are dimmed. Same format as | LineSelector | - |
addedLinesLines to show as added. Same format as | LineSelector | - |
removedLinesLines to show as removed. Same format as | LineSelector | - |
diffReads a leading | boolean | false |
notationReads | boolean | false |
highlightWordsWords or regular expressions to mark on every line. | WordMatcher[] | [] |
maxHeightCaps the height; the content scrolls inside. A number is in pixels. | string | number | - |
collapsedHeightHeight while collapsed. Add a | string | number | - |
tabindex
| number | "auto" | "auto" |
addedLabelScreen reader prefix for added lines. | string | "Added:" |
removedLabelScreen reader prefix for removed lines. | string | "Removed:" |
Slots
| Slot | Scope |
|---|---|
defaultHand-built | - |
lineReplaces the content of each generated line. | { text: string; tokens: CodeToken[]; number: number; sourceLine: number; variant: CodeBlockLineVariant; } |
CodeBlockCopyButton
Props
| Prop | Type | Default |
|---|---|---|
valueText to copy. Defaults to the code of the | string | - |
timeoutHow long the copied state lasts, in milliseconds. | number | 2000 |
labelAccessible name of the button. | string | "Copy" |
copiedLabelAccessible name and announcement after a successful copy. | string | "Copied" |
errorLabelAnnouncement when the clipboard rejects the copy. | string | "Copy failed" |
variantVisual style of the button. | "default" | "destructive" | "secondary" | "outline" | "ghost" | "link" | null | "ghost" |
sizeSize of the button. | "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | "icon-xs" |
Events
| Event | Payload |
|---|---|
copyEmitted after the text is written to the clipboard. | [value: string] |
errorEmitted when the clipboard rejects the copy. | [error: unknown] |
Slots
| Slot | Scope |
|---|---|
defaultReplaces the copy and check icons. | { copied: boolean; } |
CodeBlockExpandButton
Props
| Prop | Type | Default |
|---|---|---|
expandLabelText while the block is collapsed. | string | "Show more" |
collapseLabelText while the block is expanded. | string | "Show less" |
variantVisual style of the button. | "default" | "destructive" | "secondary" | "outline" | "ghost" | "link" | null | "ghost" |
sizeSize of the button. | "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | "xs" |
Slots
| Slot | Scope |
|---|---|
defaultReplaces the label and the chevron. | { expanded: boolean; } |
CodeBlockFooter
Slots
| Slot | Scope |
|---|---|
defaultContent below the code, such as a | - |
CodeBlockHeader
Props
| Prop | Type | Default |
|---|---|---|
asChildChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | boolean | - |
asThe element or component this component should render as. Can be overwritten by | AsTag | Component | "div" |
Slots
| Slot | Scope |
|---|---|
defaultA | - |
CodeBlockLine
Props
| Prop | Type | Default |
|---|---|---|
variantState of the line. | CodeBlockLineVariant | "default" |
focusedKeeps the line at full opacity while other lines are dimmed by focus. | boolean | false |
tokensHighlighted tokens, as returned by | CodeToken[] | - |
wordsWords or patterns to mark in the tokens. | WordMatcher[] | [] |
addedLabelScreen reader prefix for an added line. | string | "Added:" |
removedLabelScreen reader prefix for a removed line. | string | "Removed:" |
Slots
| Slot | Scope |
|---|---|
defaultLine content, used when | - |
CodeBlockTabs
Props
| Prop | Type | Default |
|---|---|---|
defaultValueThe value of the tab that should be active when initially rendered. Use when you do not need to control the state of the tabs | StringOrNumber | - |
orientationThe orientation the tabs are laid out. Mainly so arrow navigation is done accordingly (left & right vs. up & down) | DataOrientation | horizontal |
dirThe reading direction of the combobox when applicable. <br> If omitted, inherits globally from | Direction | - |
activationModeWhether a tab is activated automatically (on focus) or manually (on click). | "automatic" | "manual" | "automatic" |
modelValueThe controlled value of the tab to activate. Can be bind as | StringOrNumber | - |
unmountOnHideWhen | boolean | `true` |
asChildChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | boolean | - |
asThe element or component this component should render as. Can be overwritten by | AsTag | Component | "div" |
Events
| Event | Payload |
|---|---|
update:modelValueEvent handler called when the value changes | [payload: StringOrNumber] |
Slots
| Slot | Scope |
|---|---|
defaultA | { modelValue: string | number; } |
CodeBlockTabsContent
Props
| Prop | Type | Default |
|---|---|---|
valuerequiredA unique value that associates the content with a trigger. | StringOrNumber | - |
forceMountUsed to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. | boolean | - |
asChildChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | boolean | - |
asThe element or component this component should render as. Can be overwritten by | AsTag | Component | "div" |
Slots
| Slot | Scope |
|---|---|
defaultThe | - |
CodeBlockTabsList
Props
| Prop | Type | Default |
|---|---|---|
loopWhen | boolean | - |
asChildChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | boolean | - |
asThe element or component this component should render as. Can be overwritten by | AsTag | Component | "div" |
Slots
| Slot | Scope |
|---|---|
default
| - |
CodeBlockTabsTrigger
Props
| Prop | Type | Default |
|---|---|---|
valuerequiredA unique value that associates the trigger with a content. | StringOrNumber | - |
disabledWhen | boolean | - |
asChildChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | boolean | - |
asThe element or component this component should render as. Can be overwritten by | AsTag | Component | "div" |
Slots
| Slot | Scope |
|---|---|
defaultA file name or a package manager, with an optional leading icon. | - |
CodeBlockTitle
Props
| Prop | Type | Default |
|---|---|---|
asChildChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | boolean | - |
asThe element or component this component should render as. Can be overwritten by | AsTag | Component | "div" |
Slots
| Slot | Scope |
|---|---|
defaultA file name or a label, with an optional leading icon. Long titles truncate. | - |
CodeBlockWrapButton
Props
| Prop | Type | Default |
|---|---|---|
labelAccessible name of the toggle. Its pressed state is announced separately. | string | "Wrap lines" |
variantVisual style of the button. | "default" | "destructive" | "secondary" | "outline" | "ghost" | "link" | null | "ghost" |
sizeSize of the button. | "default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null | "icon-xs" |
Slots
| Slot | Scope |
|---|---|
defaultReplaces the wrap icon. | { wrap: boolean; } |
Accessibility
- The code is a
preelement withdir="ltr", so it reads left to right in right-to-left pages. ACodeBlockTitlenames it througharia-labelledby. - When the code scrolls, the content becomes focusable, so it can be scrolled with the arrow keys. A focus ring shows on the block.
- Line numbers and diff markers are CSS, so screen readers skip them and they are not copied. Added and removed lines start with hidden "Added:" and "Removed:" labels. Change them with
addedLabelandremovedLabel. - The copy button announces "Copied", or "Copy failed", in a live region. The wrap button is a toggle with
aria-pressed, and the expand button setsaria-expandedandaria-controls. - Floating actions are always visible on touch screens. Focus dimming and the expand icon turn off their transitions when reduced motion is requested.
- In forced colors mode, highlighted, added, and removed lines get an outline instead of a background.
| Key | Description |
|---|---|
| Tab | Moves focus to the tabs, the action buttons, and the content when it scrolls. |
| ArrowLeft or ArrowRight | Moves between tabs and selects them. |
| Home or End | Moves to the first or last tab. |
| ArrowUp or ArrowDown | Scrolls the focused content. |