Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

CSV Import Command Menu Block for React Aria

A CRM import menu hung under an Import button: three sources, a delimiter switch, a drop zone that reads the first line of a CSV in the browser, and a select per column to match it to a field.

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

Read a file's header and match its columns before importing with this import command menu for React with React Aria. It hangs under the Import button of a contacts page in Loadbay, a CRM import tool, whose header holds an outline field with its ⌘ K cap, and the page is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The menu is the catalog Command over three sources (CSV file, Spreadsheet, JSON), with a ToggleGroup for the delimiter above the field. With CSV chosen, a FileUpload drop zone under the list takes a file by drop or Browse (or a typed sample), the browser reads only its first kilobyte, and the columns are listed ("5 columns: Full name, E-mail, Phone no., ...") each with a select for the field it goes to, matched by its header where possible. Import checks the mapping and adds nothing. Spreadsheet and JSON say they are preview only. For preview the menu is drawn open in place when the page loads; the field, or Ctrl or ⌘ K while focus is in the block, opens the real command dialog with the same source, file and mapping. For import tools, uploaders and any flow where a file's shape matters before it goes in.

FAQ

Why is the menu already open, and what do I keep?

So you can see it without a click. On load the menu is the Command drawn inline over a picture of a contacts page, in a plain shell that is not modal, so nothing is trapped, hidden or focused and the page around the preview keeps working. The header's field opens the real command dialog, modal, with its focus trap, Escape and focus return, and the inline copy stays closed until a reload. Both show the same Palette and share the block's state, so a file or a mapping in the copy is in the dialog. In your app keep the dialog and drop the copy: the "in-place" state, the scrim with its shell and the Escape and blur effects.

What does the browser read, and where does it go?

Only the first 1,024 bytes of the file, with file.slice(0, 1024).text(), and only the first line of that. It never leaves the device: there is no request. The file's name and size are shown, the columns are the first line split by the delimiter (commas inside double quotes stay inside a name), and a byte-order mark is dropped. Files over 5 MB, or that are not CSV or text, are refused with the reason.

What does the delimiter switch do?

It takes the same first line apart again with a comma, a semicolon or a tab, lists the new columns and matches them afresh, so a semicolon file read as commas shows one column and switching fixes it. If nothing can be split the panel says no columns were found and to try another delimiter.

How are columns matched to fields?

By the header's words: "mail" goes to Email, "phone", "mobile" and "tel" to Phone, "name" to Name, "organi" and "company" to Company, and anything else to Skip. Each column has a native select you can change, and Import says how many columns are matched, or that Email is missing or that two columns go to one field. Selects are native so they work inside the dialog without a popover.

What stays local, and how do I wire it?

All of it, and the file is read on this device. Spreadsheet and JSON are preview only. In your app send the whole file to your import endpoint from run once the mapping is valid, show the server's row errors in the panel, and parse spreadsheets and JSON on the server.

Is it accessible from the keyboard?

Yes. Type to search, the arrow keys move through the sources, Enter chooses one, and Tab reaches the delimiter switch, Browse, Use sample, the selects and Import. Escape closes the menu with focus on the field, and Ctrl or ⌘ K opens the dialog while focus is inside the block, and only then. The rows and the empty state are menuitems of a React Aria menu inside an autocomplete; nothing is highlighted until you press an arrow key. Nothing is highlighted until you press an arrow key.