- Accordion
- Agent Thinking
- Alert
- Alert Dialog
- Announcement
- Aspect Ratio
- Attachment
- Avatar
- Badge
- Breadcrumb
- Bubble
- Button
- Button Group
- Calendar
- Card
- Carousel
- Checkbox
- Checkbox Card
- Code Block
- Collapsible
- Color Picker
- Combobox
- Command
- Composer
- Composer Loader
- Context Menu
- Data Grid
- Data Table
- Date Picker
- Dialog
- Direction
- Drawer
- Dropdown Menu
- Empty
- Field
- File Upload
- Hover Card
- Input
- Input Group
- Input Otp
- Item
- Kbd
- Label
- Marker
- Menubar
- Message
- Message Scroller
- Meter
- Native Select
- Navigation Menu
- Number Field
- Pagination
- Period Switcher
- Popover
- Progress
- Questionnaire
- Radio Card
- Radio Group
- Resizable
- Reveal
- Scroll Area
- Search Field
- Select
- Separator
- Sheet
- Sidebar
- Skeleton
- Slider
- Social Button
- Sonner
- Spinner
- Status Dot
- Switch
- Switch Card
- Table
- Tabs
- Tag Group
- Textarea
- Theme Toggle
- Time Field
- Toggle
- Toggle Group
- Tooltip
- Trace
- Tree View
- Agent Creator 01
- Agent Limits Card 01
- Agent Progress 01
- AI Chat 01
- AI Image Generation 01
- AI Profile 01
- Alerts Card 01
- Auth Card 01
- Calendar 01
- Composer 01
- Composer Attachments 01
- Composer Panel 01
- Finance Dashboard 01
- Home Dashboard 01
- Marketing Dashboard 01
- Medical Profile 01
- Meeting Scheduler 01
- Multi Agent Chat 01
- Notification Center 01
- Patient Info Card 01
- Project Board 01
- Questionnaire 01
- Settings Modal 01
- Sidebar 01
- Stat Cards 01
- Task List 01
- Web Search 01
- Activity Rings Card 01
- Area Chart Card 01
- Bar List Card 01
- Combo Chart Card 01
- Contributions Card 01
- Earnings Chart Card 01
- Funnel Chart Card 01
- Heatmap Chart Card 01
- Line Chart Card 01
- Most Active Days Card 01
- Orders Chart Card 01
- Radar Chart Card 01
- Radial Chart Card 01
- Revenue Chart Card 01
- Sankey Chart Card 01
- Scatter Chart Card 01
- Sleep Score Card 01
- Stage Bars Card 01
- Steps Card 01
greeting.ts
const greeting = "Hello, garden!";console.log(greeting);const count = 1;const count = 2;console.log(count);Unable to run this command.greeting.ts
const greeting = "Hello, garden!";
console.log(greeting);Installation
$ pnpm dlx shadcn@latest add @cobalt/code-block
Options
variant="inset"puts the code on a raised panel inside a tray. The header (title, tabs, actions) sits on the tray. The install commands on this site use it.
Source
"use client";
import { RiCheckLine, RiFileCopyLine } from "@remixicon/react";
import { cva } from "class-variance-authority";
import type { VariantProps } from "class-variance-authority";
import { cn } from "cn";
import * as React from "react";
import { Button } from "@/components/ui/button";
const codeBlockVariants = cva(
"group/code-block relative flex w-full min-w-0 flex-col overflow-hidden rounded-[8px] text-sm has-[>[data-slot=code-block-content]:focus-visible]:ring-3 has-[>[data-slot=code-block-content]:focus-visible]:ring-ring/50 data-[size=sm]:text-xs",
{
defaultVariants: {
variant: "default",
},
variants: {
variant: {
default:
"border border-border bg-background text-foreground has-[>[data-slot=code-block-content]:focus-visible]:border-ring",
destructive:
"border border-destructive/30 text-destructive dark:border-destructive/40",
// A tray around the code: the header (tabs, title) sits on the tray
// and the code is a raised panel inside it.
inset:
// overflow-visible: the code panel rounds itself, and clipping here
// would cut the copy button's focus ring at the tray edge.
"overflow-visible rounded-[16px] border-0 bg-code p-1.5 text-code-foreground has-[>[data-slot=code-block-content]:focus-visible]:border-0",
},
},
}
);
function CodeBlock({
className,
variant = "default",
size = "default",
...props
}: React.ComponentProps<"div"> &
VariantProps<typeof codeBlockVariants> & { size?: "default" | "sm" }) {
return (
<div
data-slot="code-block"
data-variant={variant}
data-size={size}
className={cn(codeBlockVariants({ variant }), className)}
{...props}
/>
);
}
function CodeBlockHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="code-block-header"
className={cn(
"flex min-w-0 shrink-0 items-center gap-2 px-4 pt-4 text-muted-foreground group-data-[size=sm]/code-block:px-3 group-data-[size=sm]/code-block:pt-3 group-data-[variant=destructive]/code-block:text-destructive group-data-[variant=inset]/code-block:px-0 group-data-[variant=inset]/code-block:pt-0 group-data-[variant=inset]/code-block:pb-1.5 *:has-[>[data-slot=code-block-title]]:min-w-0",
className
)}
{...props}
/>
);
}
function CodeBlockTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="code-block-title"
className={cn(
"min-w-0 truncate font-sans text-sm font-medium text-foreground group-data-[size=sm]/code-block:text-xs group-data-[variant=destructive]/code-block:text-destructive group-data-[variant=inset]/code-block:px-2 [&_svg]:pointer-events-none [&_svg]:inline-block [&_svg]:align-middle [&_svg:not([class*='size-'])]:size-4 group-data-[size=sm]/code-block:[&_svg:not([class*='size-'])]:size-3.5 *:[svg]:me-1.5",
className
)}
{...props}
/>
);
}
function CodeBlockActions({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="code-block-actions"
className={cn(
"-my-1 ms-auto -me-1.5 flex shrink-0 group-data-[variant=inset]/code-block:me-0 items-center gap-1 [[data-slot=code-block]>&]:absolute [[data-slot=code-block]>&]:end-3 [[data-slot=code-block]>&]:top-3 [[data-slot=code-block]>&]:z-10 [[data-slot=code-block]>&]:m-0 [[data-slot=code-block]>&]:opacity-0 [[data-slot=code-block]>&]:transition-opacity [[data-slot=code-block]>&]:duration-150 [[data-slot=code-block]>&]:motion-reduce:transition-none [[data-slot=code-block]>&]:group-focus-within/code-block:opacity-100 [[data-slot=code-block]>&]:group-hover/code-block:opacity-100 group-data-[size=sm]/code-block:[[data-slot=code-block]>&]:end-2 group-data-[size=sm]/code-block:[[data-slot=code-block]>&]:top-2 [[data-slot=code-block]>&]:pointer-coarse:opacity-100 [[data-slot=code-block]>&]:*:[button]:bg-background [[data-slot=code-block]>&]:*:[button]:ring-1 [[data-slot=code-block]>&]:*:[button]:ring-foreground/10",
className
)}
{...props}
/>
);
}
function CodeBlockCopyButton({
value,
variant = "ghost",
size = "icon-xs",
className,
onClick,
...props
}: React.ComponentProps<typeof Button> & { value: string }) {
const [copied, setCopied] = React.useState(false);
React.useEffect(() => {
if (!copied) {
return;
}
const timeout = setTimeout(() => setCopied(false), 2000);
return () => clearTimeout(timeout);
}, [copied]);
return (
<>
<Button
data-slot="code-block-copy-button"
data-copied={copied || undefined}
variant={variant}
size={size}
aria-label={copied ? "Copied" : "Copy"}
className={cn(className)}
onClick={(event) => {
onClick?.(event);
navigator.clipboard
.writeText(value)
.then(() => setCopied(true))
.catch(() => {});
}}
{...props}
>
{copied ? <RiCheckLine /> : <RiFileCopyLine />}
</Button>
<span aria-live="polite" className="sr-only">
{copied ? "Copied" : ""}
</span>
</>
);
}
function CodeBlockContent({
className,
children,
style,
wrap = false,
lineNumbers = false,
startLine = 1,
...props
}: React.ComponentProps<"pre"> & {
wrap?: boolean;
lineNumbers?: boolean;
startLine?: number;
}) {
return (
<pre
data-slot="code-block-content"
data-wrap={wrap || undefined}
data-numbered={lineNumbers || undefined}
dir="ltr"
// Scrollable code must take focus so keyboard users can scroll it.
// oxlint-disable-next-line jsx-a11y/no-noninteractive-tabindex
tabIndex={0}
style={{ counterReset: `line ${startLine - 1}`, ...style }}
className={cn(
"min-h-0 min-w-0 scroll-fade-x [scrollbar-width:thin] overflow-auto px-4 py-4 text-start font-mono leading-relaxed outline-none group-has-data-[slot=code-block-footer]/code-block:pb-2 group-has-data-[slot=code-block-header]/code-block:pt-2 group-data-[size=sm]/code-block:px-3 group-data-[size=sm]/code-block:py-3 group-data-[size=sm]/code-block:group-has-data-[slot=code-block-header]/code-block:pt-1.5 group-data-[variant=inset]/code-block:rounded-[12px] group-data-[variant=inset]/code-block:bg-background group-data-[variant=inset]/code-block:pb-3.5 group-data-[variant=inset]/code-block:shadow-[shadow:var(--shadow-control,0_1px_2px_0_rgb(0_0_0/0.05))] data-wrap:scroll-fade-y data-wrap:wrap-break-word data-wrap:whitespace-pre-wrap",
className
)}
{...props}
>
<code
data-slot="code-block-code"
className="grid min-h-lh min-w-max content-start in-data-numbered:grid-cols-[auto_1fr] in-data-wrap:min-w-0"
>
{lineNumbers && typeof children === "string"
? children
.replace(/\n$/, "")
.split("\n")
.map((line, index) => (
<CodeBlockLine key={index}>{line}</CodeBlockLine>
))
: children}
</code>
</pre>
);
}
const codeBlockLineVariants = cva(
"relative -mx-4 block min-h-lh px-4 group-data-[size=sm]/code-block:-mx-3 group-data-[size=sm]/code-block:px-3 before:min-w-6 before:text-end before:text-[color:var(--foreground-faint,var(--muted-foreground))] after:absolute after:start-1 after:top-0 after:text-muted-foreground after:select-none in-data-numbered:col-span-2 in-data-numbered:grid in-data-numbered:grid-cols-subgrid in-data-numbered:gap-x-3 in-data-numbered:[counter-increment:line] in-data-numbered:before:content-[counter(line)] in-data-numbered:before:select-none in-data-numbered:supports-[content:counter(line)_/_'']:before:content-[counter(line)_/_'']",
{
defaultVariants: {
variant: "default",
},
variants: {
variant: {
added: "bg-muted after:content-['+']",
default: "",
highlighted: "bg-muted",
removed: "bg-destructive/10 after:text-destructive after:content-['−']",
},
},
}
);
function CodeBlockLine({
className,
variant = "default",
children,
...props
}: React.ComponentProps<"span"> & VariantProps<typeof codeBlockLineVariants>) {
// An empty line holds a newline so copying a selection keeps blank lines.
const empty =
children === undefined ||
children === null ||
children === "" ||
(Array.isArray(children) && children.length === 0);
return (
<span
data-slot="code-block-line"
data-variant={variant}
className={cn(codeBlockLineVariants({ variant }), className)}
{...props}
>
<span data-slot="code-block-line-content" className="min-w-0">
{empty ? "\n" : children}
</span>
</span>
);
}
function CodeBlockFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="code-block-footer"
className={cn(
"flex min-w-0 shrink-0 items-center gap-2 px-4 pb-4 text-muted-foreground group-data-[size=sm]/code-block:px-3 group-data-[size=sm]/code-block:pb-3",
className
)}
{...props}
/>
);
}
export {
CodeBlock,
CodeBlockHeader,
CodeBlockTitle,
CodeBlockActions,
CodeBlockCopyButton,
CodeBlockContent,
CodeBlockLine,
CodeBlockFooter,
}; On this page