Code
CodeGroup, PackageManager, Snippet, FileTree, Kbd, CopyPrompt. Components for code, files, and keys.
~ 1 min read
| Component | Use for |
|---|---|
<CodeGroup> | Tab multiple code blocks; auto-syncs on known label families |
<PackageManager> | npm/yarn/pnpm/bun installs and scripts |
<Snippet> | Reuse content across pages |
<FileTree> | Render a directory tree from a Markdown list |
<Kbd> | Keyboard chord, with auto Cmd→Ctrl on non-Mac |
<CopyPrompt> | One-click “hand these instructions to my coding agent” button |
CodeGroup
Wraps two or more code blocks and renders them as tabs. Each child’s data-filename (from the title="..." fence attribute) or language hint becomes the tab label.
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | — | Sync key. Same name on multiple blocks → matched tabs across the page and across navigations (persisted in localStorage). When empty, the runtime auto-derives a name if the labels match a known family (npm/yarn/pnpm/bun, pip/uv/poetry, macOS/Linux/Windows, python/typescript, …). |
Multiple languages
<CodeGroup>
```ts title="fetch.ts"
await fetch("https://api.example.com/v1/users")
```
```py title="fetch.py"
requests.get("https://api.example.com/v1/users")
```
```sh title="curl"
curl https://api.example.com/v1/users
```
</CodeGroup>await fetch("https://api.example.com/v1/users")requests.get("https://api.example.com/v1/users")curl https://api.example.com/v1/usersSynced across blocks
Pick TypeScript on the first block, the second block flips to TypeScript too.
<CodeGroup name="lang">
```ts
console.log("hello")
```
```py
print("hello")
```
</CodeGroup>
<CodeGroup name="lang">
```ts
console.log("done")
```
```py
print("done")
```
</CodeGroup>Auto-sync (no name)
When labels match a known family, sync happens automatically. Pick bun once, every install block on the page flips. Works across pages too.
<CodeGroup>
```bash bun
bun add tangly
```
```bash npm
npm install tangly
```
```bash pnpm
pnpm add tangly
```
```bash yarn
yarn add tangly
```
</CodeGroup>Recognized families: auto:pkg-js, auto:pkg-py, auto:pkg-rust, auto:os, auto:shell, auto:runtime-js, auto:lang, auto:editor. Full list in code-sync.ts.
Tab label resolution
Per child, in priority order:
data-filename(from code-fencetitle="...")data-language(the language token after the opening fence)"Tab N"fallback
PackageManager
<PackageManager> renders an install or run command across npm, yarn, pnpm, and bun. Tabs auto-sync as auto:pkg-js so a single pick on any install block (raw <CodeGroup> or <PackageManager>) propagates everywhere.
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | — | Package name to install. Required unless command is set. |
mode | "dev" | "prod" | "prod" | "dev" installs as devDependency. |
command | string | — | Run a script instead of installing. e.g. "run build". |
syncName | string | "" | Empty default → joins the auto:pkg-js pool. Override only to break the link. |
Install
<PackageManager name="tangly" />npm install tanglyyarn add tanglypnpm add tanglybun add tanglyInstall as dev dependency
<PackageManager name="@tanglydocs/schema" mode="dev" />npm install --save-dev @tanglydocs/schemayarn add --dev @tanglydocs/schemapnpm add --save-dev @tanglydocs/schemabun add --dev @tanglydocs/schemaRun a script
<PackageManager command="run build" />npm run buildyarn run buildpnpm run buildbun run buildIndependent block
To break sync for a specific block, give it a unique syncName:
<PackageManager name="@tanglydocs/cli" syncName="cli-only" />Snippet
Reusable inline content. Phase-1 placeholder, accepts inline children for now; the file-resolved form (file="snippets/x.mdx") lands in Phase 2.
| Prop | Type | Default | Description |
|---|---|---|---|
file | string | — | Path under snippets/. Phase-2; ignored today. |
<Snippet file="snippets/install.mdx">
Inline fallback content. Renders as-is until file resolution lands.
</Snippet>For most reuse cases today, embedded blocks cover the same need.
FileTree
Turns a nested Markdown list into a styled directory tree. Indentation drives nesting. Files pick icons from their extension. **bold** highlights a row. Trailing text becomes a comment. ... is a placeholder.
| Prop | Type | Default | Description |
|---|---|---|---|
variant | "default" | "terminal" | "ascii" | "default" | Visual style. See below |
chrome | boolean | false | Only on terminal; draws three macOS-style window dots |
Default
src
components
- Button.tsx
- Card.tsx the file you’ll edit
- Note.astro
styles
- globals.css
- …
- package.json dependencies
- tangly.config.ts
<FileTree>
- src
- components
- Button.tsx
- **Card.tsx** the file you'll edit
- Note.astro
- styles
- globals.css
- ...
- package.json dependencies
- tangly.config.ts
</FileTree>Terminal variant
Drops the icons, swaps in literal ├──/└── connectors, ships a dark background that ignores theme. Like running tree in a shell. Add chrome for the macOS window header.
my-docs/
- docs.json
- introduction.mdx
guides/
- deploying.mdx
- themes.mdx
- package.json
<FileTree variant="terminal" chrome>
- my-docs/
- docs.json
- introduction.mdx
- guides/
- **deploying.mdx**
- package.json
</FileTree>ASCII variant
Minimal: no card, no background, no icons. Use when a tree sits inside another component (Card, Step, Accordion) and the boxed look would be too heavy.
packages/
theme-ui/
components/
- FileTree.astro
- rehype-file-tree.ts
- docs/
<FileTree variant="ascii">
- packages/
- theme-ui/
- components/
- **FileTree.astro**
- docs/
</FileTree>Authoring syntax
Five things to know:
- Directories. A line ending in
/is a directory; so is any line with nested children. Directories are collapsible (<details open>); readers can fold them shut. - Highlighting. Wrap a row in
**bold**to highlight it. - Comments. Anything after the filename becomes a muted comment. Inline Markdown works.
- Placeholders. A row containing only
...(or…) draws a dimmed placeholder row. - Custom icons. Prefix a row with
<Icon icon="..." />to override the auto-detected file icon.
<FileTree>
- <Icon icon="cog" /> config
- <Icon icon="database" /> data
</FileTree>Kbd
Renders styled keycap(s). Either pass children for a single key, or keys="..." for a chord. + and whitespace separate keys, and Cmd / ⌘ swap to Ctrl on non-Mac platforms.
| Prop | Type | Default | Description |
|---|---|---|---|
keys | string | — | Chord string. e.g. "Cmd+K", "Shift Tab". |
Single key
Press <Kbd>Tab</Kbd> to autocomplete.Press Tab to autocomplete.
Chord (auto Cmd→Ctrl)
Open the command palette with <Kbd keys="Cmd+K" />.Open the command palette with Cmd+K.
On Linux and Windows the same chord renders as Ctrl + K.
Manual chord
<Kbd>Shift</Kbd>+<Kbd>Tab</Kbd>Shift+Tab
CopyPrompt
A button that copies a ready-to-paste prompt for a coding agent.
The pattern it exists for: instead of pasting a wall of setup instructions at the reader, paste one line that points their agent at a URL it can fetch. The instructions then live at that URL and can change without anyone re-copying.
| Prop | Type | Default | Description |
|---|---|---|---|
href | string | — | URL of the instructions the agent should fetch. Relative is fine. |
text | string | — | Full prompt text. Wins over href when both are given. |
subject | string | — | What the instructions set up, e.g. "squirrelscan". Used in the generated sentence. |
label | string | "Copy prompt" | Button label. |
icons | string[] | [] | Small vendor marks shown before the label. Image URLs. |
title | string | — | Native tooltip. |
One of href or text is required; the build fails if both are missing.
From a URL
<CopyPrompt href="/agent-setup/prompt.md" subject="my-tool" />Copies: Fetch and follow the setup instructions for my-tool at https://your-site.dev/agent-setup/prompt.md
A relative href picks up the site’s configured base at build, then resolves against the current document in the browser at click time. The agent receiving the prompt has no page context, so it needs an absolute URL.
Custom text
<CopyPrompt text="Install the CLI with `brew install my-tool`, then run `my-tool init`." />With vendor marks
<CopyPrompt
href="/agent-setup/prompt.md"
subject="my-tool"
label="Set up my agent"
icons={["/logo/agents/claude-code.png", "/logo/agents/codex.png"]}
/>Code block chrome
Code blocks render with a caption bar carrying the filename, the language label, and the Copy button. Set styling.codeblocks in docs.json to change that site-wide:
{
"styling": {
"codeblocks": {
"chrome": "bare",
"copyButton": "hover"
}
}
}| Key | Values | Default | Description |
|---|---|---|---|
chrome | "bar" | "bare" | "bar" | bare drops the caption and floats the Copy button over the top-right of the block. |
copyButton | true | false | "always" | "hover" | true | false removes the button. "hover" (bare chrome only) reveals it on hover or keyboard focus; touch devices always see it. |
A single fence overrides chrome either way with a bare or bar meta flag:
```ts bare
const x = 1;
```