Meet Frontfriend.Your brand. Your rules. Your agents, connected.Explore what’s new
DocumentationCode Block

Code Block

Display code, logs, and diffs with optional syntax highlighting.

Interactive preview

Rendered from the shared Frontfriend stories with React Base, the same preview Studio shows. Styling comes from your Design System; the code below is native to Reka UI.

Installation

Complete project setup first. Your configuration must select vue/reka and include your Design System ID.

pnpm exec frontfriend download code-block -f vue --primitive reka

Review dependencies reported by the downloader and keep your source changes in version control.

Usage

<script setup lang="ts">
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockHeader } from "@/components/ui/code-block";
import { CodeBlockTitle } from "@/components/ui/code-block";
import { CodeBlockActions } from "@/components/ui/code-block";
import { CodeBlockCopyButton } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";
import { CodeBlockFooter } from "@/components/ui/code-block";

const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';

const args = {
  "variant": "default",
  "size": "default",
  "wrap": false,
  "lineNumbers": false,
  "startLine": 1,
  "text": code,
} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown> & {
    variant?: "default" | "destructive";
    size?: "default" | "sm";
  };
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <CodeBlock :variant="args.variant" :size="args.size"
      ><CodeBlockHeader
        ><CodeBlockTitle>example.ts</CodeBlockTitle
        ><CodeBlockActions
          ><CodeBlockCopyButton
            :value="args.text" /></CodeBlockActions></CodeBlockHeader
      ><CodeBlockContent
        :wrap="args.wrap"
        :line-numbers="args.lineNumbers"
        :start-line="args.startLine"
        aria-label="Editable code"
        >{{ args.text }}</CodeBlockContent
      ><CodeBlockFooter>Plain text</CodeBlockFooter></CodeBlock
    >
  </div>
</template>

Compose a header, title, actions, content, lines, and footer. Pass the original text to the copy button. Line numbers and diff markers are display only. Copy feedback appears after the Clipboard promise resolves and resets after two seconds. Failed copies keep the Copy label. Use line numbers with wrapping to keep the gutter aligned. Give the focusable code area a useful title or accessible label. Code stays left-to-right inside a right-to-left page.

Syntax highlighting

Highlighting is optional. Plain code does not load Shiki. A supported language loads when first used. GitHub Light and Dark supply syntax colors; the design system controls surfaces, borders, and actions. Unsupported languages and failed language loads remain plain text. Token text is escaped by the framework. The diff language colors a diff; it does not set added or removed line variants.

Helper imports

Import highlightCode and loadHighlighter from @/components/ui/code-block. Their existing helper subpaths also remain valid. highlightCode(code, language) is async and returns one highlighted line per input line, or null for an unsupported language or a failed load. It works on the server and retains final newlines. loadHighlighter(language) returns a tokenizer with highlight(code, state?) for grammar-state continuation.

Import useHighlightedCode from @/components/ui/code-block. It accepts strings, refs, or getters and returns a readonly computed value of token lines. Use CodeBlockToken with :token="token" inside each CodeBlockLine. The composable removes exactly one final newline. It caches complete lines and updates the last line as text arrives. The server renders plain lines until the composable mounts. The async helper can also be called during server rendering. Vue templates have an additional vue grammar.

Put CodeBlockLine components in the default slot of CodeBlockContent. Use string interpolation inside CodeBlockLine to preserve indentation. Bind the copy text with :value. The copy button emits click; use @click for a handler and call event.preventDefault() to cancel copying. Other Button and native pre attributes are forwarded.

Replace a stock installation

Use --replace when the CLI reports a conflict with existing files. The install replaces the UI files, CodeBlockToken.vue, highlighter.ts, and useHighlightedCode.ts in the code-block folder. The barrel exports all three runtime helpers, as shadcn-vue does. Files outside the install manifest remain unchanged.

Highlighted Vue template

<script setup lang="ts">
import { CodeBlock, CodeBlockContent, CodeBlockLine, CodeBlockToken, useHighlightedCode } from "@/components/ui/code-block";

const code = 'const message = "Hello";';
const lines = useHighlightedCode(code, "typescript");
</script>

<template>
  <CodeBlock>
    <CodeBlockContent line-numbers aria-label="TypeScript example">
      <CodeBlockLine v-for="(line, index) in lines" :key="index">
        <CodeBlockToken v-for="(token, tokenIndex) in line" :key="tokenIndex" :token="token" />
      </CodeBlockLine>
    </CodeBlockContent>
  </CodeBlock>
</template>

Examples

Default

<script setup lang="ts">
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";

const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';

const longCode =
  'const description = "A long line of plain text wraps inside the code block while each line number stays aligned with the first row of its content.";\nconsole.log(description);';

const diffCode =
  "const config = {\n  retries: 1,\n  retries: 3,\n  timeout: 2000,\n};\n";

const errorCode =
  "Error: Cannot find module ./config\n    at build (build.ts:12:3)";

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <CodeBlock
      ><CodeBlockContent aria-label="Plain code">{{
        code
      }}</CodeBlockContent></CodeBlock
    >
  </div>
</template>

Header And Footer

<script setup lang="ts">
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockHeader } from "@/components/ui/code-block";
import { CodeBlockTitle } from "@/components/ui/code-block";
import { CodeBlockActions } from "@/components/ui/code-block";
import { CodeBlockCopyButton } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";
import { CodeBlockFooter } from "@/components/ui/code-block";

const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';

const longCode =
  'const description = "A long line of plain text wraps inside the code block while each line number stays aligned with the first row of its content.";\nconsole.log(description);';

const diffCode =
  "const config = {\n  retries: 1,\n  retries: 3,\n  timeout: 2000,\n};\n";

const errorCode =
  "Error: Cannot find module ./config\n    at build (build.ts:12:3)";

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <CodeBlock
      ><CodeBlockHeader
        ><CodeBlockTitle>example.ts</CodeBlockTitle
        ><CodeBlockActions
          ><CodeBlockCopyButton
            :value="code" /></CodeBlockActions></CodeBlockHeader
      ><CodeBlockContent aria-label="Example source">{{
        code
      }}</CodeBlockContent
      ><CodeBlockFooter>TypeScript · Plain text</CodeBlockFooter></CodeBlock
    >
  </div>
</template>

Floating Actions

<script setup lang="ts">
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockActions } from "@/components/ui/code-block";
import { CodeBlockCopyButton } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";

const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';

const longCode =
  'const description = "A long line of plain text wraps inside the code block while each line number stays aligned with the first row of its content.";\nconsole.log(description);';

const diffCode =
  "const config = {\n  retries: 1,\n  retries: 3,\n  timeout: 2000,\n};\n";

const errorCode =
  "Error: Cannot find module ./config\n    at build (build.ts:12:3)";

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <CodeBlock
      ><CodeBlockActions><CodeBlockCopyButton :value="code" /></CodeBlockActions
      ><CodeBlockContent aria-label="Code with floating actions">{{
        code
      }}</CodeBlockContent></CodeBlock
    >
  </div>
</template>

Small

<script setup lang="ts">
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockHeader } from "@/components/ui/code-block";
import { CodeBlockTitle } from "@/components/ui/code-block";
import { CodeBlockActions } from "@/components/ui/code-block";
import { CodeBlockCopyButton } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";
import { CodeBlockFooter } from "@/components/ui/code-block";

const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';

const longCode =
  'const description = "A long line of plain text wraps inside the code block while each line number stays aligned with the first row of its content.";\nconsole.log(description);';

const diffCode =
  "const config = {\n  retries: 1,\n  retries: 3,\n  timeout: 2000,\n};\n";

const errorCode =
  "Error: Cannot find module ./config\n    at build (build.ts:12:3)";

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <CodeBlock size="sm"
      ><CodeBlockHeader
        ><CodeBlockTitle>compact.ts</CodeBlockTitle
        ><CodeBlockActions
          ><CodeBlockCopyButton
            :value="code" /></CodeBlockActions></CodeBlockHeader
      ><CodeBlockContent line-numbers aria-label="Compact code">{{
        code
      }}</CodeBlockContent
      ><CodeBlockFooter>3 lines</CodeBlockFooter></CodeBlock
    >
  </div>
</template>

Wrap

<script setup lang="ts">
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";

const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';

const longCode =
  'const description = "A long line of plain text wraps inside the code block while each line number stays aligned with the first row of its content.";\nconsole.log(description);';

const diffCode =
  "const config = {\n  retries: 1,\n  retries: 3,\n  timeout: 2000,\n};\n";

const errorCode =
  "Error: Cannot find module ./config\n    at build (build.ts:12:3)";

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <CodeBlock
      ><CodeBlockContent wrap aria-label="Wrapped code">{{
        longCode
      }}</CodeBlockContent></CodeBlock
    >
  </div>
</template>

Line Numbers

<script setup lang="ts">
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";

const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';

const longCode =
  'const description = "A long line of plain text wraps inside the code block while each line number stays aligned with the first row of its content.";\nconsole.log(description);';

const diffCode =
  "const config = {\n  retries: 1,\n  retries: 3,\n  timeout: 2000,\n};\n";

const errorCode =
  "Error: Cannot find module ./config\n    at build (build.ts:12:3)";

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <CodeBlock
      ><CodeBlockContent line-numbers aria-label="Numbered code">{{
        code
      }}</CodeBlockContent></CodeBlock
    >
  </div>
</template>

Start Offset

<script setup lang="ts">
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";

const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';

const longCode =
  'const description = "A long line of plain text wraps inside the code block while each line number stays aligned with the first row of its content.";\nconsole.log(description);';

const diffCode =
  "const config = {\n  retries: 1,\n  retries: 3,\n  timeout: 2000,\n};\n";

const errorCode =
  "Error: Cannot find module ./config\n    at build (build.ts:12:3)";

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <CodeBlock
      ><CodeBlockContent
        line-numbers
        :start-line="10000"
        aria-label="Code starting at line 10000"
        >{{ code }}</CodeBlockContent
      ></CodeBlock
    >
  </div>
</template>

Wrap And Numbers

<script setup lang="ts">
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";

const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';

const longCode =
  'const description = "A long line of plain text wraps inside the code block while each line number stays aligned with the first row of its content.";\nconsole.log(description);';

const diffCode =
  "const config = {\n  retries: 1,\n  retries: 3,\n  timeout: 2000,\n};\n";

const errorCode =
  "Error: Cannot find module ./config\n    at build (build.ts:12:3)";

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <CodeBlock
      ><CodeBlockContent
        wrap
        line-numbers
        :start-line="98"
        aria-label="Wrapped numbered code"
        >{{ longCode }}</CodeBlockContent
      ></CodeBlock
    >
  </div>
</template>

Diff Lines

<script setup lang="ts">
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockHeader } from "@/components/ui/code-block";
import { CodeBlockTitle } from "@/components/ui/code-block";
import { CodeBlockActions } from "@/components/ui/code-block";
import { CodeBlockCopyButton } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";
import { CodeBlockLine } from "@/components/ui/code-block";

const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';

const longCode =
  'const description = "A long line of plain text wraps inside the code block while each line number stays aligned with the first row of its content.";\nconsole.log(description);';

const diffCode =
  "const config = {\n  retries: 1,\n  retries: 3,\n  timeout: 2000,\n};\n";

const errorCode =
  "Error: Cannot find module ./config\n    at build (build.ts:12:3)";

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <CodeBlock
      ><CodeBlockHeader
        ><CodeBlockTitle>configuration.diff</CodeBlockTitle
        ><CodeBlockActions
          ><CodeBlockCopyButton
            :value="diffCode" /></CodeBlockActions></CodeBlockHeader
      ><CodeBlockContent line-numbers aria-label="Configuration diff"
        ><CodeBlockLine>const config = {</CodeBlockLine
        ><CodeBlockLine variant="removed">{{ "  retries: 1," }}</CodeBlockLine
        ><CodeBlockLine variant="added">{{ "  retries: 3," }}</CodeBlockLine
        ><CodeBlockLine variant="highlighted">{{
          "  timeout: 2000,"
        }}</CodeBlockLine
        ><CodeBlockLine>};</CodeBlockLine><CodeBlockLine /></CodeBlockContent
    ></CodeBlock>
  </div>
</template>

Destructive

<script setup lang="ts">
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockHeader } from "@/components/ui/code-block";
import { CodeBlockTitle } from "@/components/ui/code-block";
import { CodeBlockActions } from "@/components/ui/code-block";
import { CodeBlockCopyButton } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";
import { CodeBlockFooter } from "@/components/ui/code-block";

const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';

const longCode =
  'const description = "A long line of plain text wraps inside the code block while each line number stays aligned with the first row of its content.";\nconsole.log(description);';

const diffCode =
  "const config = {\n  retries: 1,\n  retries: 3,\n  timeout: 2000,\n};\n";

const errorCode =
  "Error: Cannot find module ./config\n    at build (build.ts:12:3)";

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <CodeBlock variant="destructive"
      ><CodeBlockHeader
        ><CodeBlockTitle>Build failed</CodeBlockTitle
        ><CodeBlockActions
          ><CodeBlockCopyButton
            :value="errorCode" /></CodeBlockActions></CodeBlockHeader
      ><CodeBlockContent aria-label="Build error">{{
        errorCode
      }}</CodeBlockContent
      ><CodeBlockFooter
        >Check the file path and try again.</CodeBlockFooter
      ></CodeBlock
    >
  </div>
</template>

Copy

<script setup lang="ts">
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockHeader } from "@/components/ui/code-block";
import { CodeBlockTitle } from "@/components/ui/code-block";
import { CodeBlockActions } from "@/components/ui/code-block";
import { CodeBlockCopyButton } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";

const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';

const longCode =
  'const description = "A long line of plain text wraps inside the code block while each line number stays aligned with the first row of its content.";\nconsole.log(description);';

const diffCode =
  "const config = {\n  retries: 1,\n  retries: 3,\n  timeout: 2000,\n};\n";

const errorCode =
  "Error: Cannot find module ./config\n    at build (build.ts:12:3)";

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <CodeBlock
      ><CodeBlockHeader
        ><CodeBlockTitle>Copy example</CodeBlockTitle
        ><CodeBlockActions
          ><CodeBlockCopyButton
            :value="code" /></CodeBlockActions></CodeBlockHeader
      ><CodeBlockContent line-numbers aria-label="Code to copy">{{
        code
      }}</CodeBlockContent></CodeBlock
    >
  </div>
</template>

Blank Lines

<script setup lang="ts">
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";

const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';

const longCode =
  'const description = "A long line of plain text wraps inside the code block while each line number stays aligned with the first row of its content.";\nconsole.log(description);';

const diffCode =
  "const config = {\n  retries: 1,\n  retries: 3,\n  timeout: 2000,\n};\n";

const errorCode =
  "Error: Cannot find module ./config\n    at build (build.ts:12:3)";

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <CodeBlock
      ><CodeBlockContent line-numbers aria-label="Code with blank lines">{{
        "first\n\nlast\n\n"
      }}</CodeBlockContent></CodeBlock
    >
  </div>
</template>

Right To Left

<script setup lang="ts">
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockHeader } from "@/components/ui/code-block";
import { CodeBlockTitle } from "@/components/ui/code-block";
import { CodeBlockActions } from "@/components/ui/code-block";
import { CodeBlockCopyButton } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";
import { CodeBlockFooter } from "@/components/ui/code-block";

const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';

const longCode =
  'const description = "A long line of plain text wraps inside the code block while each line number stays aligned with the first row of its content.";\nconsole.log(description);';

const diffCode =
  "const config = {\n  retries: 1,\n  retries: 3,\n  timeout: 2000,\n};\n";

const errorCode =
  "Error: Cannot find module ./config\n    at build (build.ts:12:3)";

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <div dir="rtl">
      <CodeBlock
        ><CodeBlockHeader
          ><CodeBlockTitle>مثال الشفرة</CodeBlockTitle
          ><CodeBlockActions
            ><CodeBlockCopyButton
              :value="code" /></CodeBlockActions></CodeBlockHeader
        ><CodeBlockContent line-numbers aria-label="Left-to-right code">{{
          code
        }}</CodeBlockContent
        ><CodeBlockFooter>نص عادي</CodeBlockFooter></CodeBlock
      >
    </div>
  </div>
</template>

Highlighted

<script setup lang="ts">
import { defineComponent } from "vue";
import { useHighlightedCode } from "@/components/ui/code-block";
import { h } from "vue";
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockHeader } from "@/components/ui/code-block";
import { CodeBlockTitle } from "@/components/ui/code-block";
import { CodeBlockActions } from "@/components/ui/code-block";
import { CodeBlockCopyButton } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";
import { CodeBlockLine } from "@/components/ui/code-block";
import { CodeBlockToken } from "@/components/ui/code-block";
import { CodeBlockFooter } from "@/components/ui/code-block";

const highlightedSource =
  '// Fetch one user and keep the response typed.\n// The caller handles network errors.\ntype User = { id: number; name: string };\n\n/** Read the user record from the API. */\nexport async function getUser(id: number): Promise<User> {\n  // Use the same endpoint for cached and fresh records.\n  const response = await fetch(`/api/users/${id}`);\n  if (!response.ok) {\n    throw new Error("User not found");\n  }\n\n  // Keep JSON parsing in the typed request function.\n  return response.json();\n}';

const HighlightedFile = defineComponent({
  props: {
    value: { type: String, required: true },
    language: { type: String, required: true },
    title: { type: String, required: true },
    wrap: { type: Boolean, default: false },
  },
  setup(props) {
    const lines = useHighlightedCode(
      () => props.value,
      () => props.language,
    );
    return () =>
      h(CodeBlock, () => [
        h(CodeBlockHeader, () => [
          h(CodeBlockTitle, () => props.title),
          h(CodeBlockActions, () =>
            h(CodeBlockCopyButton, { value: props.value }),
          ),
        ]),
        h(
          CodeBlockContent,
          { lineNumbers: true, wrap: props.wrap, "aria-label": props.title },
          () =>
            lines.value.map((line, index) =>
              h(CodeBlockLine, { key: index }, () =>
                line.map((token, tokenIndex) =>
                  h(CodeBlockToken, { key: tokenIndex, token }),
                ),
              ),
            ),
        ),
        h(CodeBlockFooter, () => props.language),
      ]);
  },
});

const args = {
  "wrap": false,
} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <HighlightedFile
      :value="highlightedSource"
      language="typescript"
      title="users.ts"
      :wrap="args.wrap"
    />
  </div>
</template>

Server Helper

<script setup lang="ts">
import { shallowRef } from "vue";
import type { HighlightedToken } from "@/components/ui/code-block";
import { onMounted } from "vue";
import { highlightCode } from "@/components/ui/code-block";
import { onBeforeUnmount } from "vue";
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockHeader } from "@/components/ui/code-block";
import { CodeBlockTitle } from "@/components/ui/code-block";
import { CodeBlockActions } from "@/components/ui/code-block";
import { CodeBlockCopyButton } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";
import { CodeBlockLine } from "@/components/ui/code-block";
import { CodeBlockToken } from "@/components/ui/code-block";
import { CodeBlockFooter } from "@/components/ui/code-block";

const highlightedSource =
  '// Fetch one user and keep the response typed.\n// The caller handles network errors.\ntype User = { id: number; name: string };\n\n/** Read the user record from the API. */\nexport async function getUser(id: number): Promise<User> {\n  // Use the same endpoint for cached and fresh records.\n  const response = await fetch(`/api/users/${id}`);\n  if (!response.ok) {\n    throw new Error("User not found");\n  }\n\n  // Keep JSON parsing in the typed request function.\n  return response.json();\n}';

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
const lines = shallowRef<HighlightedToken[][]>(
  highlightedSource.split("\n").map((content) => [{ content }]),
);
let active = true;
onMounted(async () => {
  const next = await highlightCode(highlightedSource, "typescript");
  if (active && next) lines.value = next;
});
onBeforeUnmount(() => {
  active = false;
});
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <CodeBlock
      ><CodeBlockHeader
        ><CodeBlockTitle>Full-file helper</CodeBlockTitle
        ><CodeBlockActions
          ><CodeBlockCopyButton
            :value="highlightedSource" /></CodeBlockActions></CodeBlockHeader
      ><CodeBlockContent line-numbers aria-label="Full highlighted source"
        ><CodeBlockLine v-for="(line, index) in lines" :key="index"
          ><CodeBlockToken
            v-for="(token, tokenIndex) in line"
            :key="tokenIndex"
            :token="token" /></CodeBlockLine></CodeBlockContent
      ><CodeBlockFooter
        >highlightCode returns token records for each line.</CodeBlockFooter
      ></CodeBlock
    >
  </div>
</template>

Streaming

<script setup lang="ts">
import { ref } from "vue";
import { computed } from "vue";
import { useHighlightedCode } from "@/components/ui/code-block";
import { onBeforeUnmount } from "vue";
import { Button } from "@/components/ui/button";
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockHeader } from "@/components/ui/code-block";
import { CodeBlockTitle } from "@/components/ui/code-block";
import { CodeBlockActions } from "@/components/ui/code-block";
import { CodeBlockCopyButton } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";
import { CodeBlockLine } from "@/components/ui/code-block";
import { CodeBlockToken } from "@/components/ui/code-block";
import { CodeBlockFooter } from "@/components/ui/code-block";

const highlightedSource =
  '// Fetch one user and keep the response typed.\n// The caller handles network errors.\ntype User = { id: number; name: string };\n\n/** Read the user record from the API. */\nexport async function getUser(id: number): Promise<User> {\n  // Use the same endpoint for cached and fresh records.\n  const response = await fetch(`/api/users/${id}`);\n  if (!response.ok) {\n    throw new Error("User not found");\n  }\n\n  // Keep JSON parsing in the typed request function.\n  return response.json();\n}';

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
const length = ref(58);
const running = ref(false);
const value = computed(() => highlightedSource.slice(0, length.value));
const lines = useHighlightedCode(value, "typescript");
let timer: ReturnType<typeof setInterval> | undefined;
function toggle() {
  clearInterval(timer);
  if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
    length.value = highlightedSource.length;
    running.value = false;
    return;
  }
  running.value = !running.value;
  if (!running.value) return;
  if (length.value === highlightedSource.length) length.value = 0;
  timer = setInterval(() => {
    length.value = Math.min(length.value + 3, highlightedSource.length);
    if (length.value === highlightedSource.length) {
      clearInterval(timer);
      running.value = false;
    }
  }, 45);
}
onBeforeUnmount(() => clearInterval(timer));
</script>

<template>
  <div
    style="
      display: grid;
      gap: 1rem;
      width: min(44rem, calc(100vw - 2rem));
      max-width: 100%;
    "
  >
    <form style="display: flex; gap: 0.5rem" @submit.prevent="toggle">
      <Button type="submit" variant="outline">{{
        running ? "Pause stream" : "Start stream"
      }}</Button>
    </form>
    <CodeBlock
      ><CodeBlockHeader
        ><CodeBlockTitle>Generated users.ts</CodeBlockTitle
        ><CodeBlockActions
          ><CodeBlockCopyButton
            :value="value" /></CodeBlockActions></CodeBlockHeader
      ><CodeBlockContent line-numbers aria-label="Streamed TypeScript"
        ><CodeBlockLine v-for="(line, index) in lines" :key="index"
          ><CodeBlockToken
            v-for="(token, tokenIndex) in line"
            :key="tokenIndex"
            :token="token" /></CodeBlockLine></CodeBlockContent
      ><CodeBlockFooter>{{
        running
          ? "Receiving code…"
          : length === highlightedSource.length
            ? "Complete"
            : "Paused"
      }}</CodeBlockFooter></CodeBlock
    >
  </div>
</template>

Multiple Languages

<script setup lang="ts">
import { defineComponent } from "vue";
import { useHighlightedCode } from "@/components/ui/code-block";
import { h } from "vue";
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockHeader } from "@/components/ui/code-block";
import { CodeBlockTitle } from "@/components/ui/code-block";
import { CodeBlockActions } from "@/components/ui/code-block";
import { CodeBlockCopyButton } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";
import { CodeBlockLine } from "@/components/ui/code-block";
import { CodeBlockToken } from "@/components/ui/code-block";
import { CodeBlockFooter } from "@/components/ui/code-block";

const highlightedSource =
  '// Fetch one user and keep the response typed.\n// The caller handles network errors.\ntype User = { id: number; name: string };\n\n/** Read the user record from the API. */\nexport async function getUser(id: number): Promise<User> {\n  // Use the same endpoint for cached and fresh records.\n  const response = await fetch(`/api/users/${id}`);\n  if (!response.ok) {\n    throw new Error("User not found");\n  }\n\n  // Keep JSON parsing in the typed request function.\n  return response.json();\n}';

const pythonSource =
  'from dataclasses import dataclass\n\n@dataclass\nclass User:\n    id: int\n    name: str\n\ndef greet(user: User) -> str:\n    return f"Hello, {user.name}!"\n\nusers = [User(1, "Ada"), User(2, "Lin")]\nfor user in users:\n    print(greet(user))';

const jsonSource =
  '{\n  "service": "users",\n  "version": 2,\n  "endpoints": [\n    { "method": "GET", "path": "/api/users/:id" },\n    { "method": "POST", "path": "/api/users" }\n  ],\n  "cache": {\n    "enabled": true,\n    "ttl": 300\n  }\n}';

const HighlightedFile = defineComponent({
  props: {
    value: { type: String, required: true },
    language: { type: String, required: true },
    title: { type: String, required: true },
    wrap: { type: Boolean, default: false },
  },
  setup(props) {
    const lines = useHighlightedCode(
      () => props.value,
      () => props.language,
    );
    return () =>
      h(CodeBlock, () => [
        h(CodeBlockHeader, () => [
          h(CodeBlockTitle, () => props.title),
          h(CodeBlockActions, () =>
            h(CodeBlockCopyButton, { value: props.value }),
          ),
        ]),
        h(
          CodeBlockContent,
          { lineNumbers: true, wrap: props.wrap, "aria-label": props.title },
          () =>
            lines.value.map((line, index) =>
              h(CodeBlockLine, { key: index }, () =>
                line.map((token, tokenIndex) =>
                  h(CodeBlockToken, { key: tokenIndex, token }),
                ),
              ),
            ),
        ),
        h(CodeBlockFooter, () => props.language),
      ]);
  },
});

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
</script>

<template>
  <div
    style="
      display: grid;
      gap: 1rem;
      width: min(48rem, calc(100vw - 2rem));
      max-width: 100%;
    "
  >
    <HighlightedFile
      :value="highlightedSource"
      language="typescript"
      title="users.ts"
    /><HighlightedFile
      :value="pythonSource"
      language="python"
      title="greet.py"
    /><HighlightedFile
      :value="jsonSource"
      language="json"
      title="service.json"
    />
  </div>
</template>

Unsupported Language

<script setup lang="ts">
import { defineComponent } from "vue";
import { useHighlightedCode } from "@/components/ui/code-block";
import { h } from "vue";
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockHeader } from "@/components/ui/code-block";
import { CodeBlockTitle } from "@/components/ui/code-block";
import { CodeBlockActions } from "@/components/ui/code-block";
import { CodeBlockCopyButton } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";
import { CodeBlockLine } from "@/components/ui/code-block";
import { CodeBlockToken } from "@/components/ui/code-block";
import { CodeBlockFooter } from "@/components/ui/code-block";

const HighlightedFile = defineComponent({
  props: {
    value: { type: String, required: true },
    language: { type: String, required: true },
    title: { type: String, required: true },
    wrap: { type: Boolean, default: false },
  },
  setup(props) {
    const lines = useHighlightedCode(
      () => props.value,
      () => props.language,
    );
    return () =>
      h(CodeBlock, () => [
        h(CodeBlockHeader, () => [
          h(CodeBlockTitle, () => props.title),
          h(CodeBlockActions, () =>
            h(CodeBlockCopyButton, { value: props.value }),
          ),
        ]),
        h(
          CodeBlockContent,
          { lineNumbers: true, wrap: props.wrap, "aria-label": props.title },
          () =>
            lines.value.map((line, index) =>
              h(CodeBlockLine, { key: index }, () =>
                line.map((token, tokenIndex) =>
                  h(CodeBlockToken, { key: tokenIndex, token }),
                ),
              ),
            ),
        ),
        h(CodeBlockFooter, () => props.language),
      ]);
  },
});

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
const value = 'TASK deploy\n  WHEN checks = passed\n  THEN publish "preview"';
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <HighlightedFile
      :value="value"
      language="custom-dsl"
      title="deploy.workflow"
    />
  </div>
</template>

Highlighted Diff

<script setup lang="ts">
import { useHighlightedCode } from "@/components/ui/code-block";
import { CodeBlock } from "@/components/ui/code-block";
import { CodeBlockHeader } from "@/components/ui/code-block";
import { CodeBlockTitle } from "@/components/ui/code-block";
import { CodeBlockActions } from "@/components/ui/code-block";
import { CodeBlockCopyButton } from "@/components/ui/code-block";
import { CodeBlockContent } from "@/components/ui/code-block";
import { CodeBlockLine } from "@/components/ui/code-block";
import { CodeBlockToken } from "@/components/ui/code-block";
import { CodeBlockFooter } from "@/components/ui/code-block";

const highlightedDiffSource =
  "const config = {\n  retries: 1, // Stop after the first failure.\n  retries: 3, // Retry temporary failures.\n  timeout: 2000, // Keep the request deadline.\n};\n";

const args = {

} satisfies Partial<
  InstanceType<typeof CodeBlock> extends { $props: infer Props } ? Props : {}
> &
  Record<string, unknown>;
const lines = useHighlightedCode(highlightedDiffSource, "typescript");
</script>

<template>
  <div style="width: min(44rem, calc(100vw - 2rem)); max-width: 100%">
    <CodeBlock
      ><CodeBlockHeader
        ><CodeBlockTitle>config.ts</CodeBlockTitle
        ><CodeBlockActions
          ><CodeBlockCopyButton
            :value="
              highlightedDiffSource
            " /></CodeBlockActions></CodeBlockHeader
      ><CodeBlockContent
        line-numbers
        aria-label="Highlighted configuration diff"
        ><CodeBlockLine
          v-for="(line, index) in lines"
          :key="index"
          :variant="
            index === 1
              ? 'removed'
              : index === 2
                ? 'added'
                : index === 3
                  ? 'highlighted'
                  : 'default'
          "
          ><CodeBlockToken
            v-for="(token, tokenIndex) in line"
            :key="tokenIndex"
            :token="token" /></CodeBlockLine></CodeBlockContent
      ><CodeBlockFooter
        >Syntax colors and explicit line variants.</CodeBlockFooter
      ></CodeBlock
    >
  </div>
</template>

API reference

Frontfriend-owned props. Native HTML and inherited primitive props remain documented by Reka UI.

highlightCode

Prop Type Default Description
code string — Source text to highlight.
language string — A supported language name or alias. Returns null for an unsupported language or a failed load.

loadHighlighter

Prop Type Default Description
language string — A supported language name or alias. Returns null for an unsupported language or a failed load.

useHighlightedCode

Prop Type Default Description
language MaybeRefOrGetter<string> — A supported language name or alias. Unsupported names return plain text in the hook.
value MaybeRefOrGetter<string> — Source text to highlight.

CodeBlockToken

Prop Type Default Description
token HighlightedToken — A token record from highlightCode or useHighlightedCode. Text is escaped; htmlStyle contains the syntax CSS variables.

CodeBlock

Prop Type Default Description
class HTMLAttributes["class"] —
size "default" | "sm" "default" The code text size and spacing.
variant "default" | "destructive" "default" The color style of the block.

CodeBlockActions

Prop Type Default Description
class HTMLAttributes["class"] —

CodeBlockContent

Prop Type Default Description
class HTMLAttributes["class"] —
lineNumbers boolean false Show line numbers. Numbers are excluded from copied text.
startLine number 1 The first line number.
wrap boolean false Wrap long lines within the block.

CodeBlockCopyButton

Prop Type Default Description
as keyof HTMLElementTagNameMap —
asChild boolean —
class HTMLAttributes["class"] —
click (event: MouseEvent) => void — Emitted when the copy button is clicked. Prevent the default event to cancel copying.
size ButtonVariants["size"] "icon-xs" The inherited Button size.
value string — The exact text to copy. Required.
variant ButtonVariants["variant"] "ghost" The inherited Button style.

CodeBlockFooter

Prop Type Default Description
class HTMLAttributes["class"] —

CodeBlockHeader

Prop Type Default Description
class HTMLAttributes["class"] —

CodeBlockLine

Prop Type Default Description
class HTMLAttributes["class"] —
variant "default" | "added" | "removed" | "highlighted" "default" The style of this line. Diff markers are excluded from copied text.

CodeBlockTitle

Prop Type Default Description
class HTMLAttributes["class"] —

Configuration determines the actual classes and available styling hooks. See Theming & component configuration before introducing a repeated local override.