Code
A fenced code block on the Noctis well surface: a header band carrying the language logo, a caption, and a copy button, over a horizontally scrollable code body. It owns its code typography — monospace family, size, line-height, and padding — so a plain <pre> reads as a properly-set block; syntax highlighting only colours the tokens. Opt into a tabbed group of samples by composing CodeBlock.Tabs.
Basic
A bare <CodeBlock> renders the default composition. Give it a label (a filename caption) or a language (used for the brand logo and, absent a label, the header text), and it draws the header band with a copy button over the code body. The copy button writes the rendered code's textContent to the clipboard unless you set copyText explicitly.
import { Button } from "@stridge/noctis";
export function Save() {
return <Button variant="primary">Save</Button>;
}"use client";
import { CodeBlock } from "@stridge/noctis";
const SNIPPET = `import { Button } from "@stridge/noctis";
export function Save() {
return <Button variant="primary">Save</Button>;
}`;
export default function CodeBlockBasic() {
return (
<div className="w-full max-w-md">
<CodeBlock label="Save.tsx" language="tsx" copyLabel="Copy" copiedLabel="Copied">
<pre className="whitespace-pre">{SNIPPET}</pre>
</CodeBlock>
</div>
);
}
Language & copy text
Pass language alone for a header titled by the language name with its brand logo, and set copyText when the on-screen markup isn't the exact text to copy — for a highlighted <pre>, omitting it copies the visible code's text content, which is usually right. Override copyLabel / copiedLabel to translate the affordance.
pnpm add @stridge/noctis
"use client";
import { CodeBlock } from "@stridge/noctis";
const SNIPPET = "pnpm add @stridge/noctis";
export default function CodeBlockComposed() {
return (
<div className="w-full max-w-md">
<CodeBlock language="bash" copyText={SNIPPET} copyLabel="Copy command" copiedLabel="Copied">
<pre className="whitespace-pre">{SNIPPET}</pre>
</CodeBlock>
</div>
);
}
Tabs
Tabbing is opt-in by composition: wrap one <CodeBlock> per sample in CodeBlock.Tabs and you get a tab strip — one tab per child, captioned and logo'd from each child's label (or language) — over a flush code body, with a single copy button that always copies whichever sample is showing. Name the strip with aria-label. The package-manager install block is the archetype.
npm install @stridge/noctis
"use client";
import { CodeBlock } from "@stridge/noctis";
const SAMPLES = [
{ manager: "npm", command: "npm install @stridge/noctis" },
{ manager: "pnpm", command: "pnpm add @stridge/noctis" },
{ manager: "yarn", command: "yarn add @stridge/noctis" },
{ manager: "bun", command: "bun add @stridge/noctis" },
];
export default function CodeTabsBasic() {
return (
<div className="w-full max-w-md">
<CodeBlock.Tabs aria-label="Package manager" copyLabel="Copy" copiedLabel="Copied">
{SAMPLES.map(({ manager, command }) => (
<CodeBlock key={manager} label={manager}>
<pre className="whitespace-pre">{command}</pre>
</CodeBlock>
))}
</CodeBlock.Tabs>
</div>
);
}
Each tab can carry a language for its brand logo and a label for the caption, so a group can switch between a component, its styles, and its usage.
import { Button } from "@stridge/noctis";
export const Save = () => <Button>Save</Button>;"use client";
import { CodeBlock } from "@stridge/noctis";
const TSX = `import { Button } from "@stridge/noctis";
export const Save = () => <Button>Save</Button>;`;
const CSS = `.save {
font-weight: 600;
border-radius: 0.5rem;
}`;
export default function CodeTabsLanguages() {
return (
<div className="w-full max-w-md">
<CodeBlock.Tabs aria-label="Source language" copyLabel="Copy" copiedLabel="Copied">
<CodeBlock language="tsx" label="Save.tsx">
<pre className="whitespace-pre">{TSX}</pre>
</CodeBlock>
<CodeBlock language="css" label="save.css">
<pre className="whitespace-pre">{CSS}</pre>
</CodeBlock>
</CodeBlock.Tabs>
</div>
);
}
Anatomy
A bare <CodeBlock> renders the default composition; compose the parts for full control. Both the default block and the explicit CodeBlock.Tabs group own the copy context internally, so CodeBlock.Body / CodeBlock.CopyButton are composed within one of them, not standalone.
CodeBlock(Root) — the well-surface chassis. Props:label,language,icon(override the brand logo),copyText,copyLabel,copiedLabel. With children only, renders the header (or a floating copy button when there's no header) over the body.CodeBlock.Header— the header band; holds the title, logo, and copy button.CodeBlock.Title— the filename / language caption, treated as code rather than prose.CodeBlock.CopyButton— the copy affordance; reads the copy target and labels from context.CodeBlock.Body— the horizontally scrollable code body, pinneddir="ltr"so tokens never reorder on an RTL page, and registered as the copy target. Owns the monospace type metrics and padding.CodeBlock.Tabs— the tab-group frame on the well surface (opt-in). Props:aria-label(names the strip),copyLabel/copiedLabel,className. Each child is a<CodeBlock>(or a raw<pre>carryinglabel/language).CodeBlock.Tab— one language tab: a brand logo plus its label, addressed byvalue; the logo de-dims when the tab is active or hovered. Passlanguage,label, or aniconoverride.CodeBlock.Panel— the active sample's panel, flattening a nestedCodeBlock's frame so the sample sits flush.
The chassis stamps data-slot="noctis-code-block" on the frame and data-codeblock for the app stylesheet to attach a line-number gutter. The tab-group frame stamps noctis-code-tabs and the panel noctis-code-tabs-panel; the strip carries data-code-tabs-strip, each tab's logo data-code-tabs-logo, and the active tab and its panel carry Base UI's data-active.
Keyboard
The tab strip is fully keyboard-operable (a plain <CodeBlock> has no interactive chrome beyond its copy button).
| Key | Action |
|---|---|
| Tab | Move focus onto the active tab in the strip. |
| ← / → | Move between tabs and switch the shown sample; arrows mirror under RTL. |
| Home / End | Jump to the first / last tab. |
On surfaces
The same control re-tuned across the elevation scopes — the root canvas, an elevated panel, a menu, and a sunken well. It stays legible on every layer.
pnpm add @stridge/noctispnpm add @stridge/noctispnpm add @stridge/noctispnpm add @stridge/noctisDesign tokens
Generated from the component's declaration — the same graph that mints the CSS, so a variable name or its resolution default can't drift. The minted tokens are the public override seam: set one on any ancestor and every code block in that region retunes — e.g. .docs { --noctis-code-block-header-height: 2.75rem; } raises every header band beneath it. Knobs that aren't minted are reached through the part's data-slot. See Customization for the full override ladder and Tokens for the whole graph.
The tab strip carries its own metrics — the tab height, strip padding, and per-tab insets.
API reference
Generated from the component's types — every prop, type, default, and description comes straight from the source. Each part gets its own table; the Root carries the label / language / copyText chrome props while the parts forward their native element attributes. Expand a row for the full type and description.
CodeBlock.Header
CodeBlock.Title
CodeBlock.Body
The tabbed parts — the opt-in CodeBlock.Tabs group and its Tabs and Panels.