shadcn.io is not affiliated with official shadcn/ui
Data Import Dialog Block for React
A CRM's empty Contacts page with a full-screen import dialog shown open: upload a CSV or use a sample, match six columns to fields with Selects, review new, duplicate and error counts with row numbers, and import with a filling bar.
Installation
Overview
Let people bring their data in without a leap of faith, with this import dialog for React with Base UI. It opens from the Contacts page of Contactloom, a CRM for small studios: the page is empty, drawn as a picture and a button under a soft scrim inside a hairline frame with a plus mark on each corner, and its header holds an outline Import contacts Button. The dialog fills the screen and walks three steps shown by Steps. Upload offers a File Upload drop zone for a CSV file and a Use sample file button. Map lists the file's six columns with a value from the first row and a Select for the field each goes to, already matched where the header gives it away and set to Don't import where not; it refuses to go on without Name and Email, or with two columns on one field. Review counts what the import would do, new contacts, duplicates skipped and rows with errors listed by row number and reason, with a Switch to skip the rows with errors. Import, the only Shine Button, says how many it will add, fills a bar, and ends on "Imported 231 contacts in this demo". The page behind it then shows the count. For preview the dialog is drawn open in place when the page loads, inset so the page shows around it; the header button opens the real full-screen Dialog, with the same steps and the same state. For CRMs, mailing lists, product catalogues and any app that starts empty.
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 page: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Import contacts button opens the real full-screen Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same ImportBody component and share the block's state, so a file chosen 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.
Is my file read? What stays local?
No. A file you drop or choose shows its real name and size, and nothing more: the demo never opens it, so the rows, columns and counts come from a typed sample of 238 contacts, and the dialog says so beside the file. Nothing is uploaded, and the contacts count on the page is local state. In your app parse the file on the client or the server, send the headers and the first rows to build the Map step, and run the real checks for the Review counts.
How does the matching work, and what is refused?
matchOf reads each header: anything with "mail" goes to Email, "phone" or "mobile" to Phone, "name" to Name, "organi" or "company" to Company, "label" or "tag" to Tags, and the rest to Don't import. You can change any of them. Next is refused, in words, with no Name, no Email (it is how duplicates are found), or two columns on the same field. The Review counts follow the mapping: leave Phone out and the row with a bad phone number is no longer an error.
What do the counts mean, and what does the switch change?
Rows in the file is 238. Duplicates skipped are the rows whose email is already in Contactloom; the demo starts with 4, and importing a second time finds the first import's rows as duplicates too. Errors are the rows listed by number with the reason. With Skip rows with errors on, they stay out; with it off they are imported with their problem fields empty, so the button's count grows by the number of errors. If nothing new remains, the button turns quiet and says why when pressed.
What happens on a phone?
The dialog is the whole screen. The step names collapse to the marker numbers with a line saying "Step 2 of 3", the mapping rows stack so each column's name, sample and Select read top to bottom, and the footer's two buttons stay on screen while the body scrolls between the steps and the footer.
Is it accessible from the keyboard?
Yes. The real dialog is named by its title and described by its sentence, focus stays inside until it closes, and Escape and the close button return focus to Import contacts; both are ignored while the bar runs. The trigger is passed to DialogTrigger through render, and the dialog is controlled with open and onOpenChange. Every Select is named for its column, the drop zone is reachable through Browse files, and the copy shown on load moves no focus.
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
Media Upload Dialog
Media and widgetsA video library with its upload dialog shown open: a drop zone for images, video and audio that names the files it turns away and why, tiles for the ones that pass with a check each, bulk removal, a total and an Add button that puts the clips in the library.
- Pro
A scaffolding tool's welcome tab with a new-project dialog shown open: a numbered rail beside four selects, a project name in kebab case and a live command in a themed mono window that the Shine Button copies to your clipboard.
- Pro
No Contacts Empty State
Team and accessA contacts book's empty state with two tabs: Add one, a name, email and company form, and Import CSV, a drop zone that reads the file's header in your browser; one button for the active tab and an address-card tray whose dashed place turns solid.
- Pro
A media library with twelve files ticked and the dialog that acts on all of them: a scrolling list of the files, four action cards, the chosen action's own control, a bar that fills, and a destructive button for delete.
- 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.