Code Block

New

Shows code with syntax highlighting, line numbers, diffs, tabs, and a copy button.

Preview ​

Installation ​

npx sigma-ui@latest add code-block

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

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

CommentEffect
// [!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:

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'].

VariableUsed for
--code-block-background, --code-block-foregroundThe block and its text.
--code-block-header, --code-block-borderThe header and the borders.
--code-block-line-numberLine 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-borderHighlighted lines.
--code-block-added, --code-block-added-border, --code-block-added-markerAdded lines.
--code-block-removed, --code-block-removed-border, --code-block-removed-markerRemoved lines.
--code-block-word, --code-block-word-borderMarked words.
--code-block-destructive-foregroundText 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.

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

ts
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

PropType Default
variant

Tone of the block. destructive is for errors and failed output.

"default" | "destructive" | null"default"
size

sm has tighter padding and smaller text, for tool output and logs.

"default" | "sm" | null"default"
wrap

Whether long lines wrap instead of scrolling. Use with v-model:wrap.

boolean-
expanded

Whether a block with collapsedHeight shows all lines. Use with v-model:expanded.

boolean-

Events

EventPayload
update:wrap

Emitted when the wrap button toggles wrapping.

[value: boolean]
update:expanded

Emitted when the expand button shows or hides lines.

[value: boolean]

Slots

SlotScope
default

Header, content, and footer parts.

-

CodeBlockActions ​

Slots

SlotScope
default

Buttons such as CodeBlockCopyButton and CodeBlockWrapButton.

-

CodeBlockContent ​

Props

PropType Default
code

The code to show. It is also what CodeBlockCopyButton copies. One trailing newline is ignored.

string-
lines

Highlighted tokens from useHighlightedCode, one array per line. Plain text is shown until they arrive.

CodeToken[][] | null-
language

Language name, set as data-language. Highlighting itself comes from useHighlightedCode.

string-
wrap

Wraps long lines instead of scrolling. Defaults to the CodeBlock wrap state.

boolean-
lineNumbers

Numbers each line. Numbers are not selected or copied.

booleanfalse
startLine

Number of the first line when lineNumbers is set.

number1
highlightLines

Lines to highlight, as "1,4-6" or [1, 4, 5, 6]. Counts lines of code from 1, whatever startLine is.

LineSelector-
focusLines

Lines to keep in focus while the others are dimmed. Same format as highlightLines.

LineSelector-
addedLines

Lines to show as added. Same format as highlightLines.

LineSelector-
removedLines

Lines to show as removed. Same format as highlightLines.

LineSelector-
diff

Reads a leading +, -, or space on each line as a diff marker and hides it.

booleanfalse
notation

Reads [!code highlight], [!code focus], [!code ++], [!code --], and [!code word:…] comments and hides them.

booleanfalse
highlightWords

Words or regular expressions to mark on every line.

WordMatcher[][]
maxHeight

Caps the height; the content scrolls inside. A number is in pixels.

string | number-
collapsedHeight

Height while collapsed. Add a CodeBlockExpandButton to show the rest.

string | number-
tabindex

auto makes the content focusable only when it scrolls, so keyboard users can scroll it.

number | "auto""auto"
addedLabel

Screen reader prefix for added lines.

string"Added:"
removedLabel

Screen reader prefix for removed lines.

string"Removed:"

Slots

SlotScope
default

Hand-built CodeBlockLine elements. When set, code is only used for copying.

-
line

Replaces the content of each generated line.

{ text: string; tokens: CodeToken[]; number: number; sourceLine: number; variant: CodeBlockLineVariant; }

CodeBlockCopyButton ​

Props

PropType Default
value

Text to copy. Defaults to the code of the CodeBlockContent in the same block, without diff markers and removed lines.

string-
timeout

How long the copied state lasts, in milliseconds.

number2000
label

Accessible name of the button.

string"Copy"
copiedLabel

Accessible name and announcement after a successful copy.

string"Copied"
errorLabel

Announcement when the clipboard rejects the copy.

string"Copy failed"
variant

Visual style of the button.

"default" | "destructive" | "secondary" | "outline" | "ghost" | "link" | null"ghost"
size

Size of the button.

"default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null"icon-xs"

Events

EventPayload
copy

Emitted after the text is written to the clipboard.

[value: string]
error

Emitted when the clipboard rejects the copy.

[error: unknown]

Slots

SlotScope
default

Replaces the copy and check icons.

{ copied: boolean; }

CodeBlockExpandButton ​

Props

PropType Default
expandLabel

Text while the block is collapsed.

string"Show more"
collapseLabel

Text while the block is expanded.

string"Show less"
variant

Visual style of the button.

"default" | "destructive" | "secondary" | "outline" | "ghost" | "link" | null"ghost"
size

Size of the button.

"default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null"xs"

Slots

SlotScope
default

Replaces the label and the chevron.

{ expanded: boolean; }

CodeBlockFooter ​

Slots

SlotScope
default

Content below the code, such as a CodeBlockExpandButton.

-

CodeBlockHeader ​

Props

PropType Default
asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

boolean-
as

The element or component this component should render as. Can be overwritten by asChild.

AsTag | Component"div"

Slots

SlotScope
default

A CodeBlockTitle or CodeBlockTabsList, then CodeBlockActions.

-

CodeBlockLine ​

Props

PropType Default
variant

State of the line. added and removed show a + or − marker.

CodeBlockLineVariant"default"
focused

Keeps the line at full opacity while other lines are dimmed by focus.

booleanfalse
tokens

Highlighted tokens, as returned by useHighlightedCode. Without tokens, the default slot is rendered.

CodeToken[]-
words

Words or patterns to mark in the tokens.

WordMatcher[][]
addedLabel

Screen reader prefix for an added line.

string"Added:"
removedLabel

Screen reader prefix for a removed line.

string"Removed:"

Slots

SlotScope
default

Line content, used when tokens is not set.

-

CodeBlockTabs ​

Props

PropType Default
defaultValue

The 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-
orientation

The orientation the tabs are laid out. Mainly so arrow navigation is done accordingly (left & right vs. up & down)

DataOrientationhorizontal
dir

The reading direction of the combobox when applicable. <br> If omitted, inherits globally from ConfigProvider or assumes LTR (left-to-right) reading mode.

Direction-
activationMode

Whether a tab is activated automatically (on focus) or manually (on click).

"automatic" | "manual""automatic"
modelValue

The controlled value of the tab to activate. Can be bind as v-model.

StringOrNumber-
unmountOnHide

When true, the element will be unmounted on closed state.

boolean`true`
asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

boolean-
as

The element or component this component should render as. Can be overwritten by asChild.

AsTag | Component"div"

Events

EventPayload
update:modelValue

Event handler called when the value changes

[payload: StringOrNumber]

Slots

SlotScope
default

A CodeBlockHeader with a CodeBlockTabsList, then one CodeBlockTabsContent per tab.

{ modelValue: string | number; }

CodeBlockTabsContent ​

Props

PropType Default
valuerequired

A unique value that associates the content with a trigger.

StringOrNumber-
forceMount

Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries.

boolean-
asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

boolean-
as

The element or component this component should render as. Can be overwritten by asChild.

AsTag | Component"div"

Slots

SlotScope
default

The CodeBlockContent of this tab.

-

CodeBlockTabsList ​

Props

PropType Default
loop

When true, keyboard navigation will loop from last tab to first, and vice versa.

boolean-
asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

boolean-
as

The element or component this component should render as. Can be overwritten by asChild.

AsTag | Component"div"

Slots

SlotScope
default

CodeBlockTabsTrigger elements.

-

CodeBlockTabsTrigger ​

Props

PropType Default
valuerequired

A unique value that associates the trigger with a content.

StringOrNumber-
disabled

When true, prevents the user from interacting with the tab.

boolean-
asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

boolean-
as

The element or component this component should render as. Can be overwritten by asChild.

AsTag | Component"div"

Slots

SlotScope
default

A file name or a package manager, with an optional leading icon.

-

CodeBlockTitle ​

Props

PropType Default
asChild

Change the default rendered element for the one passed as a child, merging their props and behavior.

Read our Composition guide for more details.

boolean-
as

The element or component this component should render as. Can be overwritten by asChild.

AsTag | Component"div"

Slots

SlotScope
default

A file name or a label, with an optional leading icon. Long titles truncate.

-

CodeBlockWrapButton ​

Props

PropType Default
label

Accessible name of the toggle. Its pressed state is announced separately.

string"Wrap lines"
variant

Visual style of the button.

"default" | "destructive" | "secondary" | "outline" | "ghost" | "link" | null"ghost"
size

Size of the button.

"default" | "xs" | "sm" | "lg" | "icon" | "icon-xs" | "icon-sm" | "icon-lg" | null"icon-xs"

Slots

SlotScope
default

Replaces the wrap icon.

{ wrap: boolean; }

Accessibility ​

  • The code is a pre element with dir="ltr", so it reads left to right in right-to-left pages. A CodeBlockTitle names it through aria-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 addedLabel and removedLabel.
  • 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 sets aria-expanded and aria-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.
KeyDescription
TabMoves focus to the tabs, the action buttons, and the content when it scrolls.
ArrowLeft or ArrowRightMoves between tabs and selects them.
Home or EndMoves to the first or last tab.
ArrowUp or ArrowDownScrolls the focused content.
Edit this page on GitHub