CodeBlockCard renders one display-ready source snippet with line numbers, plain-text copy/download, and Shiki syntax highlighting. It treats code as text: it never executes the snippet or writes it into the host project.
ts
export const welcome = "Hello"import { CodeBlockCard } from "@/components/fable-ui/code-block-card"
export function InstallCommand() {
return (
<CodeBlockCard
language="ts"
filename="welcome.ts"
code={'export const welcome = "Hello"'}
/>
)Installation#
pnpm dlx shadcn@latest add shobky/fable-ui/code-block-cardThe registry item installs:
components/fable-ui/code-block-card/index.ts
components/fable-ui/code-block-card/code-block-card.tsx
hooks/use-copy-to-clipboard.ts
lib/fable-ui/tools/show-code-block-tool.ts
lib/fable-ui/manifests/show-code-block.mdIt uses the installed shiki package plus shadcn alert, button, card, empty, and skeleton primitives, and shared Fable core.
Usage#
import { CodeBlockCard } from "@/components/fable-ui/code-block-card"
export function InstallCommand() {
return (
<CodeBlockCard
language="ts"
filename="welcome.ts"
code={'export const welcome = "Hello"'}
/>
)
}Use showLineNumbers={false} for a very small snippet. Copy and Download live in compact, accessible icon actions at the card’s logical inline end. Download uses the supplied filename when safe, or derives a file extension from the language.
Streaming highlighting#
During streaming, the card first shows a plain-text fallback, then re-highlights the latest code with the existing shiki@3.23.0 API roughly every 150ms. Each scheduled timer is cancellable; in-flight Shiki work is guarded by its code key/request id, so an old asynchronous result is ignored rather than replacing newer HTML. The completed part triggers an exact immediate highlight flush.
Successful results use a bounded, recency-refreshed cache of at most 40 entries; the oldest successful highlight is evicted as new sources arrive.
If a language is unsupported or Shiki fails, the raw code remains visible with optional line numbers. This favors an honest readable fallback over stale or missing source. The card does not use @shikijs/stream.
Tool definition#
The AI SDK tool name is show_code_block.
{
language: string
code: string
filename?: string
showLineNumbers?: boolean
}Use it for one complete or streaming code snippet that benefits from inspection, copying, or download. Do not use it for shell execution, host file writes, secrets, a source tree, rich text, or a prose draft.
import { tool } from "ai"
import { z } from "zod"
import {
CodeBlockCard,
type CodeBlockCardProps,
} from "@/components/fable-ui/code-block-card"
import { defineFableComponent } from "@/lib/fable-ui/core/definitions"
export const showCodeBlockInputSchema = z.object({
language: z.string().min(1),
code: z.string(),
filename: z.string().min(1).optional(),
showLineNumbers: z.boolean().default(true),
})
export type ShowCodeBlockInput = z.infer<typeof showCodeBlockInputSchema>
function getPartialCodeBlockProps(input: unknown): CodeBlockCardProps {
const partial =
input && typeof input === "object" ? (input as Record<string, unknown>) : {}
return {
language: typeof partial.language === "string" ? partial.language : "text",
code: typeof partial.code === "string" ? partial.code : "",
filename:
typeof partial.filename === "string" ? partial.filename : undefined,
showLineNumbers:
typeof partial.showLineNumbers === "boolean"
? partial.showLineNumbers
: true,
isStreaming: true,
}
}
export function createShowCodeBlockTool() {
return tool({
description:
"Show a complete code snippet for review, copying, or download. Include its programming language and raw source. Do not use for executable host commands, file writes, secret values, rich text, or prose drafts.",
inputSchema: showCodeBlockInputSchema,
execute: async (input) => input,
})
}
export const showCodeBlock = defineFableComponent({
name: "show_code_block",
schema: showCodeBlockInputSchema,
tool: createShowCodeBlockTool(),
renderer: {
Component: CodeBlockCard,
loadingProps: { language: "text", code: "", isLoading: true },
streamingProps: getPartialCodeBlockProps,
emptyProps: { language: "text", code: "" },
errorProps: (description, part) => ({
...getPartialCodeBlockProps(part?.input),
isStreaming: false,
error: { title: "Code block unavailable", description },
}),
toProps: (data: ShowCodeBlockInput) => data,
},
})
States#
| State | Behavior |
|---|---|
| Ready | Renders the final Shiki highlight or raw fallback, with Copy and Download enabled for source. |
| Loading | Shows a preparing status and skeleton. |
| Streaming | Keeps the source selectable, begins plain, and coalesces live Shiki updates about every 150ms. |
| Empty | Explains that no source is available. |
| Error | Shows the error and retains any available code as raw, copyable, downloadable text. |
| Disabled | Marks the snippet read-only; code remains copyable and downloadable because no execution or mutation is involved. |
Direction and accessibility#
Metadata inherits the host direction while the source surface is explicitly LTR, including inside RTL documents. Icon actions have accessible names and tooltips, loading status is announced, and line numbers are visual metadata rather than a replacement for code text.
Manifest and caveats#
---
tool: show_code_block
type: registry:component
---
# show_code_block
Use `show_code_block` for a display-ready code snippet that benefits from syntax highlighting, copying, or download. During streaming it can show a readable raw fallback while the current code is highlighted.
Provide raw `code` and a short `language` identifier such as `ts`, `tsx`, `python`, or `sql`. Optionally provide `filename` and set `showLineNumbers: false` for very small snippets.
Avoid it for commands that should execute automatically, file writes, secrets, long prose, rich text, or partial host-owned source trees. The card renders source as text and never executes it.
Syntax highlighting is presentation only. It cannot prove the code compiles, is safe to execute, or has access to a host runtime.