Pro

Copy field

Read-only value with a Copy button whose icon turns into a check and says “Copied” to screen readers. A secret masks as a fixed row of dots that gives away nothing about its length.

Share Saturday supper

RSVP link
https://supperclub.example/rsvp/saturday-supper-14-alder-lane?from=maya

Anyone with the link can RSVP until Friday at noon.

Door code
4471#

For the guests only

Wi-Fi password
lemon-tart-1182Hidden

Priya asked; it’s on the place cards too.

Calendar feed
https://supperclub.example/cal/feeds/7f3a9c2e41b8d06e5a1f.icsHidden

Adds every supper to a calendar. Rotate it if it leaks.

Installation

With UI Polish connected, ask your agent:

Add the UI Polish copy field to this project.

Or with the shadcn CLI:

npx shadcn@latest add https://uipolish.com/r/copy-field.json
Package@base-ui/react
Brings inField, Icons, Text, Tooltip

A read-only value with a Copy button inside the field: a link to share, a key, a code. On copy the icon turns into a check for two seconds and a status line says "Copied" to screen readers. A secret shows a fixed row of dots, the same for every secret, so the mask gives nothing away about its length; a Show button reveals it in place, and Copy copies the real value either way. The value stays on one line: a long one fades out at its cut end, shows in full on hover (title), and one press selects all of it.

TSX
<CopyField  label="RSVP link"  mono  value="https://supperclub.example/rsvp/saturday-supper"  hint="Anyone with the link can RSVP until Friday at noon."/>

When to use it

  • A value the user needs to take somewhere else: an invitation link, an API key, a webhook secret, a door code.
  • Secrets that should stay hidden on screen until asked for, but copy in one press.

When not to

  • A value the user can edit: use a text field.
  • Code of more than one line: use a code block, which has its own Copy.
  • A secret that must never be shown again after it's made: show it once, in a dialog, with a warning, and keep only a masked hint afterwards.

Props

PropWhat it does
labelThe visible label. Also names the buttons: "Copy RSVP link", "Show Wi-Fi password"
valueWhat's shown, and what Copy puts on the clipboard
monoSet the value in the monospace face, a step smaller: keys, tokens, URLs, hashes
sizesm 32px, md 40px (default), lg 48px: the control heights. Give the action button the same size
secretMask the value behind 16 dots, with a Show button
revealed, defaultRevealed, onRevealedChangeControlled or uncontrolled reveal, for a secret
hintGuidance under the field. A failed copy says so in its place
onCopiedCalled once the value is on the clipboard
actionA control beside the field, the field's height: a "Rotate" or "Regenerate" button

The action slot. action sits to the right of the field, on the field's row, 8px from it. Pass a full-height button so the two line up: <Button variant="secondary">Rotate</Button> (with size matching the field's). It's outside the field, not inside it, because a pill button can't nest in the field's corners. When the action replaces the value, change value and the field updates in place; say what happened in hint ("New link made. The old one stopped working.").

States

StateWhat you seeHow
RestHairline edge, the value, a copy icon
Hover on a buttonThat button's fill and icon darken; the field's edge doesn't change, since the field itself does nothing@media (hover: hover)
CopiedCopy icon turns into a check for 2 seconds; tooltip "Copied"; "Copied" announcedrole="status"
Copy failedThe icon turns into an alert in the danger color for 4 seconds. A shown value is selected, so the copy shortcut works on it at once; the hint line, if there is one, says how
Secret, hidden16 dots in the muted color, a Show (eye) buttonsecret
Secret, shownThe value crossfades in where the dots were; the eye gets its slashthe button's aria-pressed="true"
Long valueOne line; the end fades out over 24px, only when it's cut. The full value is the titletext truncate
Focus on a buttonA line just inside the button's edge:focus-visible

Selecting by hand. The value is real text, selectable with user-select: all, so one press selects the whole value, including the part that's cut off, and it copies whole. The dots aren't selectable: copying them would only paste dots.

Layout

The field is a control height, 40px by default (32px sm, 48px lg), and fills its row. The value is one line, centered on the field's middle; the text starts 14px in, where a text field's does. The buttons sit concentrically inside: 4px from the top, bottom and end at every size (so 24, 32 or 40px), with the field's radius less those 4px, and 2px apart. On touch each hit area reaches out to the field's edge. The value takes the rest of the row and truncates with a fade; the field never grows with it, and the value never wraps or spills out of it. A small field sets the value a step smaller (13px).

Motion

WhatDurationEasing
Copy → check, check → copy (Icon)see the icon set's guide
Dots and value crossfade: leaving--ui-dur-instant--ui-ease-color
Dots and value crossfade: arriving, 60ms after--ui-dur-fast--ui-ease-enter
Eye → eye-off, the slash draws acrosssee the icon set's guide
Button hover color--ui-dur-instant--ui-ease-color
Button press release--ui-dur-snap--ui-ease-snap

Nothing moves. Showing a secret swaps the text in the same cell; the field keeps its size. Under prefers-reduced-motion: reduce icon changes land at once and the crossfade stays (it's short feedback, not motion).

Accessibility

  • The field is a group named by the label. The value is plain text that a screen reader reads in place; masked, it reads "Hidden".
  • The buttons are named from the label: "Copy RSVP link", "Show Wi-Fi password". The tooltips only repeat them, so they don't describe.
  • Show is a toggle with aria-pressed and aria-controls pointing at the value. Its name stays "Show …"; the pressed state carries the change.
  • "Copied", or the failure, goes to a role="status" region; with a hint, the failure is in the hint's live line instead. An icon change alone would be silent.
  • The clipboard is feature-detected; a failure is never silent.

Before / after

Before: a disabled input, which is hard to read and can't be selected, a password-type mask that shows exactly how long the key is, and a copy button that does nothing visible and nothing audible.

TSX
<input disabled type={shown ? "text" : "password"} value={key} /><button onClick={() => navigator.clipboard.writeText(key)}>Copy</button>

After:

TSX
<CopyField  label="Calendar feed"  secret  mono  value={feedUrl}  hint="Adds every supper to a calendar. Rotate it if it leaks."  action={<Button variant="secondary" busy={rotating} onClick={rotate}>Rotate</Button>}/>