shadcn.io is not affiliated with official shadcn/ui
Copy To Clipboard Dialog Block for React
A stock spreadsheet with its copy-as dialog shown open: the same four selected rows as Markdown, CSV, JSON or plain text, a preview that shows exactly what lands on the clipboard, a header-row option and a button that writes it.
Installation
Overview
Paste the same rows into whatever comes next, with this copy-as dialog for React with Base UI. It opens from the Q3 stock count of Gridnote, a spreadsheet app: the header holds an outline Copy as Button, and the sheet, a toolbar over a six-column grid with four selected rows, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The Dialog offers the rows in four formats through a Segment Group: Markdown, CSV, JSON and Plain text, which is tab-separated so it pastes into another sheet as cells. A sentence says what each format is for, a checkbox includes the header row where the format allows it, and a monospace preview rewrites on every pick and shows exactly what goes on the clipboard: CSV fields with commas or quotes are quoted, pipes are escaped in Markdown. Copy 4 rows as CSV, the only Shine Button, writes the real clipboard, its label follows the format, and it shows Copied for a moment. The selection on the sheet behind turns into a dashed marquee. For preview the dialog is drawn open in place when the page loads; the header button opens the real Dialog.
FAQ
Why is the dialog already open, and what do I keep?
So you can see it without a click. On load the dialog is a copy drawn in place over the sheet: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Copy as button opens the real Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same CopyFields component and share the block's state, so a format picked in the copy is there in the real one. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.
Does it really copy, and what if the browser refuses?
Yes: Copy writes the preview text to the clipboard with the browser's clipboard API, which needs a secure page (HTTPS or localhost) and, in some browsers, a press as its trigger, which this is. If the browser refuses, the button says it couldn't copy, an alert line asks you to select the preview and copy it yourself, and a polite message says so. After a success the button shows Copied and the number of rows for a few seconds, then returns to its label; nothing closes by itself.
How are the formats built?
Pure functions over the typed rows, one per format. csvOf quotes a field when it holds a comma, a quote or a line break, doubles the quotes inside it and puts an apostrophe in front of a field that starts with a formula sign, so a spreadsheet never runs it. markdownOf writes a pipe table with a separator row and right-aligned numbers. jsonOf writes an array of objects with named fields. textOf joins cells with tabs. Swap ROWS and COLUMNS for your selection and add formats as entries in CHOICES.
Why can't I turn the header row off for Markdown and JSON?
A Markdown table needs its header row to be a table, and JSON names every field in every object, so there is no header to leave out. For those two the checkbox is ticked, disabled and says "always, here". For CSV and Plain text it is yours to choose, and the preview and the line count change with it.
Is it accessible from the keyboard?
Yes. The formats are one radio group, so the arrow keys move between them, the checkbox is a real one, and the preview is a focusable region you can scroll with the arrow keys. A polite message reports each copy, and the dialog has its own copy of that message while it is open. The trigger is passed to DialogTrigger through render, and the dialog is controlled with open and onOpenChange. Focus returns to Copy as when the dialog closes.
How does it look on a phone?
The copy shown on load docks to the foot of the page as a bottom sheet, its actions pinned at the bottom with the copy button on top, and the formats in two rows, the checkbox and the preview scrolling above. The preview scrolls sideways inside its box rather than wrapping. The real dialog keeps the Dialog's own phone layout: a sheet stuck to the bottom of the screen.
Related
- Pro
A usage-report tool's Exports page with its export dialog shown open: a CSV or JSON format, a range with row counts, six fields behind a mixed Select all box and a size estimate, and a Download button that saves a real file.
- Pro
API Key Dialog
Account and securityA keys page with its create-key dialog shown open: a name, scopes and an expiry in the first step, then the secret shown once, masked behind an eye button, with a Copy button and a box that has to be ticked before Done.
- Pro
Share Link Dialog
Share and inviteA prototype file with a share dialog shown open: a switch for anyone with the link, a read-only link whose role and expiry show in its suffix, a Copy link button that really copies, and a viewer preview that turns the file behind the dialog read-only.
- Pro
A site-builder page with its embed dialog shown open: a pasted YouTube or Vimeo link detected by pattern, options for autoplay, controls, loop and start time, a drawn poster that reflects them, and the embed code to copy or insert.
- Pro
Clipboard Denied Error
Browser and deviceA panel for when the browser blocks copying: a read-only box that selects itself, Select all text, a Copy button that really tries the clipboard, the keys as words for Mac or Windows, and steps to allow it.
- Pro
Add Card Dialog
Payments and plansA transit wallet's payment methods page with its add-card dialog shown open: a card drawn in greys that fills in as you type, a number that groups itself and names its network, checks on the number, expiry and code, and an Add card button that keeps only the last four digits.