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 React Aria.

Installation

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

pnpm exec frontfriend download code-block -f react --primitive aria

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

Usage

import {
  CodeBlock,
  CodeBlockHeader,
  CodeBlockTitle,
  CodeBlockActions,
  CodeBlockCopyButton,
  CodeBlockContent,
  CodeBlockFooter,
} from "@/components/ui/code-block";
const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';
export default function CodeBlockExample() {
  const source = code;
  return (
    <div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
      <CodeBlock
        variant="default"
        size="default"
      >
        <CodeBlockHeader>
          <CodeBlockTitle>example.ts</CodeBlockTitle>
          <CodeBlockActions>
            <CodeBlockCopyButton value={source} />
          </CodeBlockActions>
        </CodeBlockHeader>
        <CodeBlockContent
          wrap={false}
          lineNumbers={false}
          startLine={1}
          aria-label="Editable code"
        >
          {source}
        </CodeBlockContent>
        <CodeBlockFooter>Plain text</CodeBlockFooter>
      </CodeBlock>
    </div>
  );
}

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/highlighter. The helpers stay separate from the UI entry. 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/use-highlighted-code. Pass the current code and language strings. Render each returned line inside CodeBlockLine. The hook removes exactly one final newline, caches complete lines, and updates the last line as text arrives. The server renders plain lines until the hook mounts. For server rendering with colors, await highlightCode from its own subpath.

The copy button uses onPress. 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 code-block.tsx and delivers both helpers in the code-block folder. Update imports from @/lib/highlighter to @/components/ui/code-block/highlighter, and from @/hooks/use-highlighted-code to @/components/ui/code-block/use-highlighted-code. The old lib/highlighter.tsx and hooks/use-highlighted-code.ts files remain outside the install manifest. Remove them after all callers use the new paths.

Examples

Default

import { CodeBlock, CodeBlockContent } from "@/components/ui/code-block";
const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
  <CodeBlock>
    <CodeBlockContent aria-label="Plain code">{code}</CodeBlockContent>
  </CodeBlock>
</div>;

Header And Footer

import {
  CodeBlock,
  CodeBlockHeader,
  CodeBlockTitle,
  CodeBlockActions,
  CodeBlockCopyButton,
  CodeBlockContent,
  CodeBlockFooter,
} from "@/components/ui/code-block";
const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "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>;

Floating Actions

import {
  CodeBlock,
  CodeBlockActions,
  CodeBlockCopyButton,
  CodeBlockContent,
} from "@/components/ui/code-block";
const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
  <CodeBlock>
    <CodeBlockActions>
      <CodeBlockCopyButton value={code} />
    </CodeBlockActions>
    <CodeBlockContent aria-label="Code with floating actions">
      {code}
    </CodeBlockContent>
  </CodeBlock>
</div>;

Small

import {
  CodeBlock,
  CodeBlockHeader,
  CodeBlockTitle,
  CodeBlockActions,
  CodeBlockCopyButton,
  CodeBlockContent,
  CodeBlockFooter,
} from "@/components/ui/code-block";
const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
  <CodeBlock size="sm">
    <CodeBlockHeader>
      <CodeBlockTitle>compact.ts</CodeBlockTitle>
      <CodeBlockActions>
        <CodeBlockCopyButton value={code} />
      </CodeBlockActions>
    </CodeBlockHeader>
    <CodeBlockContent lineNumbers aria-label="Compact code">
      {code}
    </CodeBlockContent>
    <CodeBlockFooter>3 lines</CodeBlockFooter>
  </CodeBlock>
</div>;

Wrap

import { CodeBlock, CodeBlockContent } from "@/components/ui/code-block";
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);';
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
  <CodeBlock>
    <CodeBlockContent wrap aria-label="Wrapped code">
      {longCode}
    </CodeBlockContent>
  </CodeBlock>
</div>;

Line Numbers

import { CodeBlock, CodeBlockContent } from "@/components/ui/code-block";
const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
  <CodeBlock>
    <CodeBlockContent lineNumbers aria-label="Numbered code">
      {code}
    </CodeBlockContent>
  </CodeBlock>
</div>;

Start Offset

import { CodeBlock, CodeBlockContent } from "@/components/ui/code-block";
const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
  <CodeBlock>
    <CodeBlockContent
      lineNumbers
      startLine={10000}
      aria-label="Code starting at line 10000"
    >
      {code}
    </CodeBlockContent>
  </CodeBlock>
</div>;

Wrap And Numbers

import { CodeBlock, CodeBlockContent } from "@/components/ui/code-block";
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);';
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
  <CodeBlock>
    <CodeBlockContent
      wrap
      lineNumbers
      startLine={98}
      aria-label="Wrapped numbered code"
    >
      {longCode}
    </CodeBlockContent>
  </CodeBlock>
</div>;

Diff Lines

import {
  CodeBlock,
  CodeBlockHeader,
  CodeBlockTitle,
  CodeBlockActions,
  CodeBlockCopyButton,
  CodeBlockContent,
  CodeBlockLine,
} from "@/components/ui/code-block";
const diffCode =
  "const config = {\n  retries: 1,\n  retries: 3,\n  timeout: 2000,\n};\n";
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
  <CodeBlock>
    <CodeBlockHeader>
      <CodeBlockTitle>configuration.diff</CodeBlockTitle>
      <CodeBlockActions>
        <CodeBlockCopyButton value={diffCode} />
      </CodeBlockActions>
    </CodeBlockHeader>
    <CodeBlockContent lineNumbers 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>;

Destructive

import {
  CodeBlock,
  CodeBlockHeader,
  CodeBlockTitle,
  CodeBlockActions,
  CodeBlockCopyButton,
  CodeBlockContent,
  CodeBlockFooter,
} from "@/components/ui/code-block";
const errorCode =
  "Error: Cannot find module ./config\n    at build (build.ts:12:3)";
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "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>;

Copy

import {
  CodeBlock,
  CodeBlockHeader,
  CodeBlockTitle,
  CodeBlockActions,
  CodeBlockCopyButton,
  CodeBlockContent,
} from "@/components/ui/code-block";
const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
  <CodeBlock>
    <CodeBlockHeader>
      <CodeBlockTitle>Copy example</CodeBlockTitle>
      <CodeBlockActions>
        <CodeBlockCopyButton value={code} />
      </CodeBlockActions>
    </CodeBlockHeader>
    <CodeBlockContent lineNumbers aria-label="Code to copy">
      {code}
    </CodeBlockContent>
  </CodeBlock>
</div>;

Blank Lines

import { CodeBlock, CodeBlockContent } from "@/components/ui/code-block";
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
  <CodeBlock>
    <CodeBlockContent lineNumbers aria-label="Code with blank lines">
      {"first\n\nlast\n\n"}
    </CodeBlockContent>
  </CodeBlock>
</div>;

Right To Left

import {
  CodeBlock,
  CodeBlockHeader,
  CodeBlockTitle,
  CodeBlockActions,
  CodeBlockCopyButton,
  CodeBlockContent,
  CodeBlockFooter,
} from "@/components/ui/code-block";
const code = 'const message = "Hello, Frontfriend";\n\nconsole.log(message);';
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
  <div dir="rtl">
    <CodeBlock>
      <CodeBlockHeader>
        <CodeBlockTitle>مثال الشفرة</CodeBlockTitle>
        <CodeBlockActions>
          <CodeBlockCopyButton value={code} />
        </CodeBlockActions>
      </CodeBlockHeader>
      <CodeBlockContent lineNumbers aria-label="Left-to-right code">
        {code}
      </CodeBlockContent>
      <CodeBlockFooter>نص عادي</CodeBlockFooter>
    </CodeBlock>
  </div>
</div>;

Highlighted

import { useHighlightedCode } from "@/components/ui/code-block/use-highlighted-code";
import {
  CodeBlock,
  CodeBlockHeader,
  CodeBlockTitle,
  CodeBlockActions,
  CodeBlockCopyButton,
  CodeBlockContent,
  CodeBlockLine,
  CodeBlockFooter,
} from "@/components/ui/code-block";
function HighlightedFile({
  value,
  language,
  title,
  wrap = false,
}: {
  value: string;
  language: string;
  title: string;
  wrap?: boolean;
}) {
  const lines = useHighlightedCode(value, language);
  return (
    <CodeBlock>
      <CodeBlockHeader>
        <CodeBlockTitle>{title}</CodeBlockTitle>
        <CodeBlockActions>
          <CodeBlockCopyButton value={value} />
        </CodeBlockActions>
      </CodeBlockHeader>
      <CodeBlockContent lineNumbers wrap={wrap} aria-label={title}>
        {lines.map((line, index) => (
          <CodeBlockLine key={index}>{line}</CodeBlockLine>
        ))}
      </CodeBlockContent>
      <CodeBlockFooter>{language}</CodeBlockFooter>
    </CodeBlock>
  );
}
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}';
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
  <HighlightedFile
    value={highlightedSource}
    language="typescript"
    title="users.ts"
    wrap={false}
  />
</div>;

Server Helper

import { highlightCode } from "@/components/ui/code-block/highlighter";
import {
  CodeBlock,
  CodeBlockHeader,
  CodeBlockTitle,
  CodeBlockActions,
  CodeBlockCopyButton,
  CodeBlockContent,
  CodeBlockLine,
  CodeBlockFooter,
} from "@/components/ui/code-block";
import * as React from "react";
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}';
function ServerHelperExample() {
  const [lines, setLines] = React.useState<React.ReactNode[] | null>(null);
  React.useEffect(() => {
    let active = true;
    void highlightCode(highlightedSource, "typescript").then((next) => {
      if (active) setLines(next);
    });
    return () => {
      active = false;
    };
  }, []);
  return (
    <CodeBlock>
      <CodeBlockHeader>
        <CodeBlockTitle>Full-file helper</CodeBlockTitle>
        <CodeBlockActions>
          <CodeBlockCopyButton value={highlightedSource} />
        </CodeBlockActions>
      </CodeBlockHeader>
      <CodeBlockContent lineNumbers aria-label="Full highlighted source">
        {(lines ?? highlightedSource.split("\n")).map((line, index) => (
          <CodeBlockLine key={index}>{line}</CodeBlockLine>
        ))}
      </CodeBlockContent>
      <CodeBlockFooter>
        highlightCode returns one node array per line.
      </CodeBlockFooter>
    </CodeBlock>
  );
}
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
  <ServerHelperExample />
</div>;

Streaming

import { useHighlightedCode } from "@/components/ui/code-block/use-highlighted-code";
import { Button } from "@/components/ui/button";
import {
  CodeBlock,
  CodeBlockHeader,
  CodeBlockTitle,
  CodeBlockActions,
  CodeBlockCopyButton,
  CodeBlockContent,
  CodeBlockLine,
  CodeBlockFooter,
} from "@/components/ui/code-block";
import * as React from "react";
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}';
export default function CodeBlockExample() {
  const [length, setLength] = React.useState(58);
  const [running, setRunning] = React.useState(false);
  const value = highlightedSource.slice(0, length);
  const lines = useHighlightedCode(value, "typescript");
  React.useEffect(() => {
    if (!running) return;
    const timer = setInterval(
      () =>
        setLength((current) => Math.min(current + 3, highlightedSource.length)),
      45,
    );
    return () => clearInterval(timer);
  }, [running]);
  React.useEffect(() => {
    if (length === highlightedSource.length) setRunning(false);
  }, [length]);
  return (
    <div
      style={{
        display: "grid",
        gap: "1rem",
        width: "min(44rem, calc(100vw - 2rem))",
        maxWidth: "100%",
      }}
    >
      <form
        style={{ display: "flex", gap: "0.5rem" }}
        onSubmit={(event) => {
          event.preventDefault();
          if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
            setLength(highlightedSource.length);
            setRunning(false);
            return;
          }
          if (length === highlightedSource.length) setLength(0);
          setRunning((current) => !current);
        }}
      >
        <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 lineNumbers aria-label="Streamed TypeScript">
          {lines.map((line, index) => (
            <CodeBlockLine key={index}>{line}</CodeBlockLine>
          ))}
        </CodeBlockContent>
        <CodeBlockFooter>
          {running
            ? "Receiving code…"
            : length === highlightedSource.length
              ? "Complete"
              : "Paused"}
        </CodeBlockFooter>
      </CodeBlock>
    </div>
  );
}

Multiple Languages

import { useHighlightedCode } from "@/components/ui/code-block/use-highlighted-code";
import {
  CodeBlock,
  CodeBlockHeader,
  CodeBlockTitle,
  CodeBlockActions,
  CodeBlockCopyButton,
  CodeBlockContent,
  CodeBlockLine,
  CodeBlockFooter,
} from "@/components/ui/code-block";
function HighlightedFile({
  value,
  language,
  title,
  wrap = false,
}: {
  value: string;
  language: string;
  title: string;
  wrap?: boolean;
}) {
  const lines = useHighlightedCode(value, language);
  return (
    <CodeBlock>
      <CodeBlockHeader>
        <CodeBlockTitle>{title}</CodeBlockTitle>
        <CodeBlockActions>
          <CodeBlockCopyButton value={value} />
        </CodeBlockActions>
      </CodeBlockHeader>
      <CodeBlockContent lineNumbers wrap={wrap} aria-label={title}>
        {lines.map((line, index) => (
          <CodeBlockLine key={index}>{line}</CodeBlockLine>
        ))}
      </CodeBlockContent>
      <CodeBlockFooter>{language}</CodeBlockFooter>
    </CodeBlock>
  );
}
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}';
<div
  style={{
    display: "grid",
    gap: "1rem",
    width: "min(48rem, calc(100vw - 2rem))",
    maxWidth: "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>;

Unsupported Language

import { useHighlightedCode } from "@/components/ui/code-block/use-highlighted-code";
import {
  CodeBlock,
  CodeBlockHeader,
  CodeBlockTitle,
  CodeBlockActions,
  CodeBlockCopyButton,
  CodeBlockContent,
  CodeBlockLine,
  CodeBlockFooter,
} from "@/components/ui/code-block";
function HighlightedFile({
  value,
  language,
  title,
  wrap = false,
}: {
  value: string;
  language: string;
  title: string;
  wrap?: boolean;
}) {
  const lines = useHighlightedCode(value, language);
  return (
    <CodeBlock>
      <CodeBlockHeader>
        <CodeBlockTitle>{title}</CodeBlockTitle>
        <CodeBlockActions>
          <CodeBlockCopyButton value={value} />
        </CodeBlockActions>
      </CodeBlockHeader>
      <CodeBlockContent lineNumbers wrap={wrap} aria-label={title}>
        {lines.map((line, index) => (
          <CodeBlockLine key={index}>{line}</CodeBlockLine>
        ))}
      </CodeBlockContent>
      <CodeBlockFooter>{language}</CodeBlockFooter>
    </CodeBlock>
  );
}
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
  <HighlightedFile
    value={'TASK deploy\n  WHEN checks = passed\n  THEN publish "preview"'}
    language="custom-dsl"
    title="deploy.workflow"
  />
</div>;

Highlighted Diff

import { useHighlightedCode } from "@/components/ui/code-block/use-highlighted-code";
import {
  CodeBlock,
  CodeBlockHeader,
  CodeBlockTitle,
  CodeBlockActions,
  CodeBlockCopyButton,
  CodeBlockContent,
  CodeBlockLine,
  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";
function HighlightedDiffExample() {
  const lines = useHighlightedCode(highlightedDiffSource, "typescript");
  return (
    <CodeBlock>
      <CodeBlockHeader>
        <CodeBlockTitle>config.ts</CodeBlockTitle>
        <CodeBlockActions>
          <CodeBlockCopyButton value={highlightedDiffSource} />
        </CodeBlockActions>
      </CodeBlockHeader>
      <CodeBlockContent lineNumbers aria-label="Highlighted configuration diff">
        {lines.map((line, index) => (
          <CodeBlockLine
            key={index}
            variant={
              index === 1
                ? "removed"
                : index === 2
                  ? "added"
                  : index === 3
                    ? "highlighted"
                    : "default"
            }
          >
            {line}
          </CodeBlockLine>
        ))}
      </CodeBlockContent>
      <CodeBlockFooter>
        Syntax colors and explicit line variants.
      </CodeBlockFooter>
    </CodeBlock>
  );
}
<div style={{ width: "min(44rem, calc(100vw - 2rem))", maxWidth: "100%" }}>
  <HighlightedDiffExample />
</div>;

API reference

Frontfriend-owned props. Native HTML and inherited primitive props remain documented by React Aria.

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 string — A supported language name or alias. Unsupported names return plain text in the hook.
value string — Source text to highlight.

CodeBlock

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

CodeBlockContent

Prop Type Default Description
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
onPress React.ComponentProps<typeof Button>["onPress"] — Called when the copy button is pressed with a pointer or keyboard.
size React.ComponentProps<typeof Button>["size"] "icon-xs" The inherited Button size.
value string — The exact text to copy. Required.
variant React.ComponentProps<typeof Button>["variant"] "ghost" The inherited Button style.

CodeBlockLine

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

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