Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Data Import Dialog Block for Solid

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.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Let people bring their data in without a leap of faith, with this import dialog for Solid with Ark 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 the button wrapped in DialogTrigger with asChild, 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.