Copy button
Copy a value in one press and see whether it reached the clipboard.
INV-2040-0613
Invoice was sent to Acme Corp on 3 October.
<script setup lang="ts">import { UiCard, UiStack, UiCopyButton, UiField, UiButtonGroup, UiInput } from 'grani'</script> <template> <UiCard title="INV-2040-0613" class="w-full max-w-md"> <UiStack> <p class="text-sm text-fg-muted"> Invoice <UiCopyButton value="INV-2040-0613" label="INV-2040-0613" variant="inline" ariaLabel="Copy INV-2040-0613" /> was sent to Acme Corp on 3 October. </p> <UiField label="Share link"> <UiButtonGroup> <UiInput model-value="https://example.test/share/41" readonly /> <UiCopyButton value="https://example.test/share/41" variant="outline" ariaLabel="Copy the share link" /> </UiButtonGroup> </UiField> </UiStack> </UiCard></template>
Also called copy to clipboard, clipboard button.
Usage
<UiCopyButton :value="value" /> With GraniResolver in the Vite config, a template needs no import: set it up once.
API
| Name · type | Default | Description |
|---|---|---|
| Behavior | ||
| value*string | What goes to the clipboard, whole. Empty, the button is off and says there is nothing to copy. | |
| labelstring | Visible text beside the glyph, and the name as it is ("Copy link"); for a value shown as itself pass ariaLabel too. It wins over truncate. | |
| truncatenumber | Shows value cut in the middle to about this many characters, in monospace; the copy is still whole. | |
| flushboolean= false | false | In a cell: the text starts on the column line, the button's own padding and border taken back. ghost only. |
| disabledReasonstring | Why it cannot copy now, while disabled: it stays in the Tab order and says the reason in a tooltip and as its description, as UiButton's. An empty value says "Nothing to copy" by itself. | |
| toastboolean= false | false | Also say a success as a toast, where the button leaves the view once pressed; a failure always is one. |
| Look | ||
| variantinline | outline | ghost= "ghost" | "ghost" | ghost is text alone, its glyph on hover, for an id in a table; outline is a framed button with its glyph always shown; inline is text inside a line of text, its size and ink, with a small glyph after it. |
| sizexs | sm | md | Step of the control ladder. Unset, the app's size (createGrani({ size })), sm by default. | |
| iconComponent | The glyph at rest, a Lucide component; Copy when unset. Two copy buttons side by side tell what they copy by it: Code, Bot. | |
| Form and access | ||
| disabledboolean= false | false | No copy; the button stays in place. |
| ariaLabelstring | Accessible name; defaults to "Copy" for a glyph alone and "Copy <text>" for a truncated value. | |
Anything else lands on the root element: native attributes, aria-*, data-testid.
When to use another component
| When you need | Take |
|---|---|
| A glyph that does anything else | Icon button |
| An action with text | Button |
| A value cut to fit with nothing to copy | Truncate |
| A secret shown as text on the screen | Never; it is copied with neither label nor truncate |