Snippet
Migration guide for Snippet from HeroUI v2 to v3
The Snippet component has been removed in HeroUI v3. Use native HTML elements with Tailwind CSS classes and implement copy functionality manually using the Clipboard API.
Key Changes
1. Component Removal
v2: <Snippet> component from @heroui/react
v3: Native HTML elements (<pre>, <code>) with manual copy implementation
2. Features Mapping
The v2 Snippet component had the following features that need to be replaced:
| v2 Feature | v3 Equivalent | Notes |
|---|---|---|
| Copy button | Manual Button + Clipboard API | Use navigator.clipboard.writeText() |
| Copy tooltip | Tooltip component | Use v3 Tooltip component |
| Symbol prefix | Manual rendering | Add symbol as text content |
| Multi-line support | Array mapping | Map over array of strings |
Variants (flat, solid, bordered, shadow) | Tailwind classes | Use background/border utilities |
Colors (default, primary, etc.) | Tailwind classes | Use color utilities |
Sizes (sm, md, lg) | Tailwind text sizes | Use text-sm, text-base, text-lg |
| Radius | Tailwind border radius | Use rounded-* classes |
Structure Changes
In v2, Snippet was a component wrapper with built-in copy functionality:
import { Snippet } from "@heroui/react";
export default function App() {
return (
<Snippet symbol="$">
npm install @heroui/react
</Snippet>
);
}In v3, use native HTML elements with manual copy implementation:
import { Button, Tooltip } from "@heroui/react";
import { useState } from "react";
export default function App() {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
await navigator.clipboard.writeText("npm install @heroui/react");
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<div className="flex items-center gap-2 rounded-lg bg-default-100 px-3 py-1.5">
<pre className="text-sm font-mono">
<span className="text-default-500">$ </span>
npm install @heroui/react
</pre>
<Tooltip>
<Button
isIconOnly
aria-label="Copy"
size="sm"
variant="ghost"
onPress={handleCopy}
>
{copied ? "✓" : "📋"}
</Button>
<Tooltip.Content>{copied ? "Copied!" : "Copy to clipboard"}</Tooltip.Content>
</Tooltip>
</div>
);
}Migration Examples
Multi-line Snippet
<Snippet symbol="$">
{[
"npm install @heroui/react",
"yarn add @heroui/react",
"pnpm add @heroui/react"
]}
</Snippet>import { Button, Tooltip } from "@heroui/react";
import { useState } from "react";
function MultiLineSnippet() {
const [copied, setCopied] = useState(false);
const lines = [
"npm install @heroui/react",
"yarn add @heroui/react",
"pnpm add @heroui/react"
];
const codeString = lines.join("\n");
const handleCopy = async () => {
await navigator.clipboard.writeText(codeString);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
};
return (
<div className="flex items-start gap-2 rounded-lg bg-default-100 p-3">
<div className="flex-1 space-y-1">
{lines.map((line, index) => (
<pre key={index} className="text-sm font-mono">
<span className="text-default-500">$ </span>
{line}
</pre>
))}
</div>
<Tooltip>
<Button
isIconOnly
aria-label="Copy"
size="sm"
variant="ghost"
onPress={handleCopy}
>
{copied ? "✓" : "📋"}
</Button>
<Tooltip.Content>{copied ? "Copied!" : "Copy to clipboard"}</Tooltip.Content>
</Tooltip>
</div>
);
}Styling Options
{/* With variants */}
<Snippet variant="bordered" color="primary">
npm install @heroui/react
</Snippet>
{/* Without symbol */}
<Snippet hideSymbol>
npm install @heroui/react
</Snippet>
{/* Without copy button */}
<Snippet hideCopyButton>
npm install @heroui/react
</Snippet>{/* With variants */}
<div className="flex items-center gap-2 rounded-lg border border-accent bg-transparent px-3 py-1.5">
<pre className="text-sm font-mono text-accent">
<span className="text-accent/60">$ </span>
npm install @heroui/react
</pre>
{/* Copy button */}
</div>
{/* Without symbol */}
<div className="flex items-center gap-2 rounded-lg bg-default-100 px-3 py-1.5">
<pre className="text-sm font-mono">
npm install @heroui/react
</pre>
{/* Copy button */}
</div>
{/* Without copy button */}
<div className="rounded-lg bg-default-100 px-3 py-1.5">
<pre className="text-sm font-mono">
<span className="text-default-500">$ </span>
npm install @heroui/react
</pre>
</div>Creating a Reusable Snippet Component (Recommended)
Since Snippet functionality is commonly needed, here's a complete reusable component:
import { Snippet } from "@heroui/react";
<Snippet
symbol="$"
variant="bordered"
color="primary"
size="md"
>
npm install @heroui/react
</Snippet>import { Button, Tooltip } from "@heroui/react";
import { useState, ReactNode } from "react";
import { cn } from "@/lib/utils"; // or your cn utility
interface SnippetProps {
children: string | string[];
symbol?: string | ReactNode;
variant?: "flat" | "solid" | "bordered" | "shadow";
color?: "default" | "primary" | "secondary" | "success" | "warning" | "danger";
size?: "sm" | "md" | "lg";
radius?: "none" | "sm" | "md" | "lg" | "full";
hideSymbol?: boolean;
hideCopyButton?: boolean;
disableCopy?: boolean;
disableTooltip?: boolean;
className?: string;
codeString?: string;
onCopy?: (value: string) => void;
}
const variantClasses = {
flat: "bg-default-100",
solid: "bg-default-200",
bordered: "border border-default-200 bg-transparent",
shadow: "bg-default-100 shadow-sm",
};
const colorClasses = {
default: "text-default-foreground",
primary: "text-accent",
secondary: "text-default-600",
success: "text-success",
warning: "text-warning",
danger: "text-danger",
};
const sizeClasses = {
sm: "px-1.5 py-0.5 text-xs",
md: "px-3 py-1.5 text-sm",
lg: "px-4 py-2 text-base",
};
const radiusClasses = {
none: "rounded-none",
sm: "rounded-sm",
md: "rounded-md",
lg: "rounded-lg",
full: "rounded-full",
};
export function Snippet({
children,
symbol = "$",
variant = "flat",
color = "default",
size = "md",
radius = "md",
hideSymbol = false,
hideCopyButton = false,
disableCopy = false,
disableTooltip = false,
className,
codeString,
onCopy,
}: SnippetProps) {
const [copied, setCopied] = useState(false);
const isMultiLine = Array.isArray(children);
const lines = isMultiLine ? children : [children];
const textToCopy = codeString || (isMultiLine ? lines.join("\n") : String(children));
const handleCopy = async () => {
if (disableCopy) return;
try {
await navigator.clipboard.writeText(textToCopy);
setCopied(true);
setTimeout(() => setCopied(false), 2000);
onCopy?.(textToCopy);
} catch (error) {
console.error("Failed to copy:", error);
}
};
const symbolElement = hideSymbol ? null : (
<span className={cn("text-default-500", colorClasses[color], "opacity-60")}>
{symbol}{typeof symbol === "string" ? " " : ""}
</span>
);
const copyButton = hideCopyButton ? null : (
<Tooltip isDisabled={disableTooltip || disableCopy}>
<Button
isIconOnly
aria-label="Copy"
size="sm"
variant="ghost"
onPress={handleCopy}
isDisabled={disableCopy}
className="shrink-0"
>
{copied ? (
<span className="text-success">✓</span>
) : (
<span>📋</span>
)}
</Button>
<Tooltip.Content>{copied ? "Copied!" : "Copy to clipboard"}</Tooltip.Content>
</Tooltip>
);
return (
<div
className={cn(
"flex items-start gap-2 font-mono",
variantClasses[variant],
sizeClasses[size],
radiusClasses[radius],
className
)}
>
<div className="flex-1 min-w-0">
{isMultiLine ? (
<div className="space-y-1">
{lines.map((line, index) => (
<pre key={index} className={cn("m-0", colorClasses[color])}>
{symbolElement}
{line}
</pre>
))}
</div>
) : (
<pre className={cn("m-0", colorClasses[color])}>
{symbolElement}
{children}
</pre>
)}
</div>
{copyButton}
</div>
);
}
// Usage
<Snippet
symbol="$"
variant="bordered"
color="primary"
size="md"
>
npm install @heroui/react
</Snippet>Summary
- Component Removed:
Snippetcomponent no longer exists in v3 - Import Change: Remove
import { Snippet } from "@heroui/react" - Use Native Elements: Replace with native
<pre>and<code>elements - Manual Copy: Implement copy functionality using Clipboard API
- Styling: Apply Tailwind CSS classes directly for variants, colors, sizes
- Tooltip: Use v3 Tooltip component for copy button tooltips
- Button: Use v3 Button component for copy button
Migration Steps
- Remove Import: Remove
Snippetfrom@heroui/reactimports - Replace Component: Replace all
<Snippet>instances with native HTML elements - Add Copy Functionality: Implement copy using
navigator.clipboard.writeText() - Add Copy Button: Use v3 Button component with Tooltip
- Apply Styling: Use Tailwind CSS classes for variants, colors, sizes
- Handle Multi-line: Map over arrays if multi-line snippets are needed
- Optional: Create reusable Snippet component for your application
Clipboard API Notes
The Clipboard API requires:
- HTTPS (or localhost for development)
- User interaction (can't be called automatically)
- Browser support (modern browsers)
For fallback support:
const handleCopy = async (text: string) => {
try {
await navigator.clipboard.writeText(text);
} catch (error) {
// Fallback for older browsers
const textArea = document.createElement("textarea");
textArea.value = text;
textArea.style.position = "fixed";
textArea.style.opacity = "0";
document.body.appendChild(textArea);
textArea.select();
document.execCommand("copy");
document.body.removeChild(textArea);
}
};