- Accordion
- Action Bar
- Action Toast
- Alert Banner
- Alert Dialog
- Alert
- Alphabet Index
- Amount Field
- Aspect Ratio
- Attachment
- Availability List
- Avatar
- Badge
- Bank Card
- Breadcrumb
- Bubble
- Button Group
- Button
- Calendar
- Camera Frame
- Card Design Picker
- Card Details
- Card Peek
- Card Stack
- Card
- Carousel
- Chart
- Chat Thread
- Checkbox
- Chip Group
- Choice Tile
- Code Block
- Code Dots
- Collapsible
- Combobox
- Command
- Context Menu
- Copy Field
- Countdown
- Country Select
- Data Table
- Date Field
- Date Picker
- Date Range Picker
- Dialog
- Direction
- Document Paper
- Drawer
- Dropdown Menu
- Empty
- Entity Avatar
- Field
- File Trigger
- File Upload Item
- Hover Card
- Input Group
- Input OTP
- Input
- Item
- Kbd
- Key Value
- Keypad
- Label
- Live Region
- Marker
- Message Scroller
- Message
- Money Amount
- Native Select
- Number Field
- Pagination
- Percent Split Editor
- Phone Field
- Popover
- Progress
- QR Code
- Questionnaire
- Radio Group
- Range Histogram
- Rating
- Resizable
- Scroll Area
- Search Field
- Select
- Separator
- Share Bar
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Sortable List
- Spinner
- Status Timeline
- Step Header
- Steps List
- Swipe Row
- Switch
- Table
- Tabs
- Text Field
- Textarea
- Tier Ladder
- Toggle Group
- Toggle
- Token Search Field
- Tooltip
- Trace
- Transaction Row
- Usage Meter
import { CopyField } from "@/components/ui/copy-field"
export function CopyFieldDemo() {The CopyField component shows one value with its label and a button that copies it. Use it for bank details, card numbers, wallet addresses and payment references that people paste into another app. Stack several in a card or sheet; the field has no background of its own. Pick it over a read-only Input when the value is only there to be copied, and over a KeyValue row when copying is the main action.
Installation#
pnpm dlx shadcn@latest add @dots/copy-field
Usage#
import { CopyField } from "@/components/ui/copy-field"<CopyField label="Routing number" value="021000021" mono />Features#
- Copies the exact
valuewhile showing a friendlierdisplayValue - Monospace, tabular digits for numbers and codes that wrap at any character
- Masking that hides all but the last characters and still copies the real value
- A large size for one-time and pairing codes
- The button icon turns to a check on success and an alert on failure, then resets after 2 seconds
- Copy results are announced through a polite live region
- A disabled state that keeps the value readable
Mono#
Set mono for account numbers, IBANs and addresses. Pass the raw string as value and a grouped version as displayValue, so people read groups and paste digits.
import { CopyField } from "@/components/ui/copy-field"
export function CopyFieldMono() {Masked#
Set masked to hide card numbers and security codes. All but the last visibleChars characters turn into dots, spacing is kept, and the copy button still writes the full value. Use visibleChars={0} to hide the whole value, for example a CVC.
import { CopyField } from "@/components/ui/copy-field"
export function CopyFieldMasked() {Large#
Use size="lg" for verification and pairing codes that people copy or type on another device. Add mono for codes with letters.
import { CopyField } from "@/components/ui/copy-field"
export function CopyFieldLarge() {Hidden label#
Set hideLabel when a heading or line of text above already says what the value is. The label stays available to screen readers and still names the copy button.
Enter this code on your other device
import { CopyField } from "@/components/ui/copy-field"
export function CopyFieldHiddenLabel() {Disabled#
Set isDisabled when the value can no longer be copied, such as a number that is no longer linked to the account. The button is disabled and the value turns muted but stays readable.
import { CopyField } from "@/components/ui/copy-field"
export function CopyFieldDisabled() {Copy callback#
Use onCopy to react after a successful copy, for example to log that a link was copied. Change the button verb with copyLabel, for example when you translate the UI.
Not copied yet
"use client"
import * as React from "react"Accessibility#
The copy button is a React Aria Button, so it works with mouse, touch and keyboard. Its accessible name is built from copyLabel and label, for example "Copy routing number". Acronyms such as "IBAN" keep their case.
- Always pass a
labelthat says what the value is. It names both the value and the button, even withhideLabel. - After each attempt a polite live region says "Copied" or "Couldn't copy". It speaks again on repeated copies.
- When
maskedis set, the dots are hidden from screen readers and replaced with "Hidden, ending in 4242", so the secret is never read aloud. - If the clipboard API is missing or the write is rejected, the field shows the failed state instead of failing silently.
API Reference#
CopyField#
One copyable value with its label. Accepts all div props except children and onCopy.
| Prop | Type | Default | Description |
|---|---|---|---|
label | string | - | Required. What the value is, e.g. "Card number". Also names the copy button. |
value | string | - | Required. The exact string written to the clipboard. |
displayValue | string | - | What to show instead of the raw value, e.g. a grouped card number. |
mono | boolean | false | Monospace, tabular digits. Long values wrap at any character. |
masked | boolean | false | Hide all but the last characters of what is shown. Copy still writes the real value. |
visibleChars | number | 4 | How many trailing characters stay visible when masked. 0 hides the whole value. |
onCopy | (value: string) => void | - | Called with the copied value after a successful copy. |
copyLabel | string | "Copy" | Verb for the copy button. The label is appended, e.g. "Copy card number". |
hideLabel | boolean | false | Keep the label for screen readers only. |
isDisabled | boolean | false | Disable the button and mute the value. |
size | "default" | "lg" | "default" | "lg" shows the value large with tabular digits, for codes. |
className | string | - | Additional classes to apply to the root element. |