CSV Import Kit
v0.1.1Generic modal shell and building blocks for a CSV bulk-import flow: drop zone, review-mode toggles, banners, success state, and a labeled select for review-row fields.
Install
npx shadcn@4.1.2 add @signalos/csv-import-kitRequires a configured @signalos registry and a valid SIGNALOS_REGISTRY_TOKEN - get access. Registry dependencies (@signalos/tokens, @signalos/utils, @signalos/button, @signalos/switch, @signalos/select, @signalos/confirm-dialog) are pulled automatically.
Preview
Example & code
csv-import-kit.example.tsx
// Example: a minimal CSV import flow - upload, then a success state.
import { useState } from "react"
import { CsvImportKitDropZone } from "@/components/widgets/csv-import-kit/components/CsvImportKitDropZone"
import { CsvImportKitSuccessState } from "@/components/widgets/csv-import-kit/components/CsvImportKitSuccessState"
import { CsvImportKit } from "@/components/widgets/csv-import-kit/CsvImportKit"
export default function Example() {
const [open, setOpen] = useState(true)
const [file, setFile] = useState<File | null>(null)
return (
<CsvImportKit
open={open}
onClose={() => setOpen(false)}
title="Import Users"
description="Add multiple users from a CSV file."
>
{file ? (
<CsvImportKitSuccessState
title="Import completed successfully"
description={`${file.name} was imported successfully.`}
onReset={() => setFile(null)}
/>
) : (
<CsvImportKitDropZone
isProcessing={false}
hint="Required: full_name, email, role"
onFile={setFile}
/>
)}
</CsvImportKit>
)
}
Props
CsvImportKitProps
| Prop | Type | Default | Description |
|---|---|---|---|
| open* | boolean | - | Whether the modal is open. Renders nothing when `false`. |
| onClose* | () => void | - | Fired to request the modal close immediately (no dirty-close guard). |
| title* | ReactNode | - | Modal title, e.g. "Import Users". |
| description | ReactNode | - | Modal subtitle shown under the title. |
| isWide | boolean | undefined | false | Widens the modal for wide content such as a review grid. |
| isSubmitting | boolean | undefined | false | Disables the close/expand controls while a submission is in flight. |
| isDirty | boolean | undefined | false | When `true`, closing the modal first asks the user to confirm via the built-in discard-changes dialog instead of closing immediately. |
| discardTitle | string | undefined | "Discard unsaved changes?" | |
| discardDescription | ReactNode | "Your changes will be lost." | |
| discardConfirmLabel | ReactNode | "Discard changes" | |
| discardCancelLabel | ReactNode | "Keep editing" | |
| children | ReactNode | - | Step content rendered in the scrollable body. |
| footer | ReactNode | - | Sticky footer content, e.g. `CsvImportKitFooter`. Omit to render no footer. |
| className | string | undefined | - | Extra classes merged onto the root overlay element. |
| data-testid | string | undefined | - | Test identifier rendered as `data-testid` on the dialog element. |
CsvImportKitDropZoneProps
| Prop | Type | Default | Description |
|---|---|---|---|
| title | string | undefined | "Upload your CSV file" | |
| description | string | undefined | "Drag and drop or browse from your computer" | |
| hint | string | undefined | - | Short hint about required/optional columns, shown under the drop area. |
| isProcessing* | boolean | - | Disables the drop zone and shows a processing indicator. |
| onFile* | (file: File) => void | - | Fired with the dropped or browsed file once its extension passes the `.csv` check. |
| onInvalidFile | ((reason: string) => void) | undefined | - | Fired instead of `onFile` when the selected file fails the `.csv` extension check. |
| onDownloadTemplate | (() => void) | undefined | - | Renders a "Download template" action when provided. |
| formatNote | { title: string; description: ReactNode; } | undefined | - | Expands the template action into a labeled note above the drop zone. |
| className | string | undefined | - | |
| data-testid | string | undefined | - |
CsvImportKitSelectedFileProps
| Prop | Type | Default | Description |
|---|---|---|---|
| file* | File | - | The selected file, used for its name only. |
| onRemove* | () => void | - | Fired when the remove action is activated. |
| fileTypeLabel | string | undefined | "CSV file" | Label shown under the file name. |
| className | string | undefined | - | |
| data-testid | string | undefined | - |
CsvImportKitBannerProps
| Prop | Type | Default | Description |
|---|---|---|---|
| message* | ReactNode | - | Body message. |
| onDismiss* | () => void | - | Fired when the dismiss action is activated. |
| tone | CsvImportKitBannerTone | undefined | "error" | |
| title | ReactNode | - | Overrides the tone's default heading. |
| className | string | undefined | - | |
| data-testid | string | undefined | - |
CsvImportKitSuccessStat
| Prop | Type | Default | Description |
|---|---|---|---|
| label* | string | - | |
| value* | number | - |
CsvImportKitSuccessStateProps
| Prop | Type | Default | Description |
|---|---|---|---|
| title* | ReactNode | - | |
| description | ReactNode | - | |
| onReset* | () => void | - | Fired when the reset action is activated. |
| resetLabel | ReactNode | "Upload another file" | |
| stats | CsvImportKitSuccessStat[] | undefined | - | Optional stat tiles summarizing the completed import. |
| className | string | undefined | - | |
| data-testid | string | undefined | - |
CsvImportKitFooterProps
| Prop | Type | Default | Description |
|---|---|---|---|
| onCancel* | () => void | - | Fired when the cancel action is activated. |
| onSubmit* | () => void | - | Fired when the submit action is activated. |
| isSubmitting* | boolean | - | Disables cancel and shows a spinner + `submittingLabel` on submit. |
| disabled* | boolean | - | Disables the submit action independent of `isSubmitting`. |
| submitLabel* | ReactNode | - | Submit action label when idle. |
| cancelLabel | ReactNode | "Cancel" | |
| submittingLabel | ReactNode | "Importing..." | Submit action label while `isSubmitting`. |
| className | string | undefined | - | |
| data-testid | string | undefined | - |
CsvImportKitModeOption
| Prop | Type | Default | Description |
|---|---|---|---|
| value* | CsvImportKitReviewMode | - | |
| title* | ReactNode | - | |
| description* | ReactNode | - |
CsvImportKitModeToggleProps
| Prop | Type | Default | Description |
|---|---|---|---|
| mode* | CsvImportKitReviewMode | - | |
| onChange* | (mode: CsvImportKitReviewMode) => void | - | |
| options* | [CsvImportKitModeOption, CsvImportKitModeOption] | - | The two options rendered, in order. The caller composes their own copy (e.g. counts of invalid/existing rows) rather than the toggle knowing about review-row shapes. |
| ariaLabel | string | undefined | "How to handle rows with errors" | Accessible label for the radio group. |
| disabled | boolean | undefined | - | |
| className | string | undefined | - | |
| data-testid | string | undefined | - |
CsvImportKitUpdatesToggleProps
| Prop | Type | Default | Description |
|---|---|---|---|
| title* | ReactNode | - | Heading, e.g. "Also update 3 existing records". |
| description* | ReactNode | - | Helper copy shown under the heading; typically differs by `checked`. |
| checked* | boolean | - | |
| onCheckedChange* | (checked: boolean) => void | - | |
| icon | ReactNode | - | Icon rendered in the leading badge. Omit for no icon. |
| disabled | boolean | undefined | - | |
| className | string | undefined | - | |
| data-testid | string | undefined | - |
CsvImportKitValuesBannerProps
| Prop | Type | Default | Description |
|---|---|---|---|
| targetIds* | readonly string[] | - | Ids of the rows missing a value, e.g. row ids with a blank password cell. |
| title* | ReactNode | - | Heading, e.g. "3 rows need a password". Recomputed by the caller as `targetIds` changes. |
| description* | ReactNode | - | Helper copy explaining what the action does. |
| actionLabel* | ReactNode | - | Label for the action button when idle, e.g. "Generate all". |
| icon | ReactNode | - | Icon rendered in the leading badge and on the action button. |
| onGenerate* | (ids: readonly string[]) => void | Promise<void> | - | Produces the value for each target id and applies them in one update. Deliberately synchronous-per-id, single async apply: filling values in a loop against a single-row update callback tends to revalidate against a stale row list, so this hands back every id/value pair at once. |
| disabled | boolean | undefined | - | |
| className | string | undefined | - | |
| data-testid | string | undefined | - |
CsvImportKitSelectOption
| Prop | Type | Default | Description |
|---|---|---|---|
| value* | string | - | Unique option value. |
| label | string | undefined | - | Rendered label. Falls back to `value` when omitted. |
| source | string | undefined | - | Short badge shown after the label, e.g. "from CSV". |
CsvImportKitSelectProps
| Prop | Type | Default | Description |
|---|---|---|---|
| value | string | undefined | - | |
| onChange* | (value: string) => void | - | |
| options* | readonly CsvImportKitSelectOption[] | - | Options in display order. De-duplicated by `value` (case-insensitive), first occurrence wins. |
| placeholder | string | undefined | "Select value" | |
| clearLabel | string | undefined | - | Rendered as a value-less option and used as the "cleared" placeholder. Omit to require a value. |
| error | string | undefined | - | |
| disabled | boolean | undefined | - | |
| className | string | undefined | - | |
| data-testid | string | undefined | - |
Supporting types
export type CsvImportKitBannerTone = "error" | "warning"
export type CsvImportKitReviewMode = "fix" | "skip"
npm dependencies
lucide-react@^1.7.0Changelog
csv-import-kit
0.1.1
- Initial release: a generic modal shell and building-block set for a CSV bulk-import flow - drop zone, selected-file chip, error/warning banner, success state, sticky footer, fix/skip mode toggle, apply-updates toggle, bulk-fill-blank-field banner, and a labeled/deduplicated select for review rows - plus CSV escaping/export and secure temporary-value helpers.