# Import mapping

A React component in [Beamline](https://beamline.io/)'s [Data & tables](https://beamline.io/components/data) category. Live demo: https://beamline.io/components/import-mapping

Map the columns of a file to the fields of one record type, see what every value becomes, then run the import and get an outcome for each row: suggestions are hints until accepted, ambiguous dates are named, and only failed rows are retried.

## Use it for

- Importing a spreadsheet or export into one record type (accounts, contacts, products) after the application has read the file.
- Any column-to-field mapping where values are converted and some may fail: dates, people, amounts.
- Large or nested destinations (dozens of fields, grouped paths) that need search and a destination checklist.

## Not for

- Choosing or uploading the file itself → [file-dropzone](https://beamline.io/components/file-dropzone) (place it before this).
- Reviewing proposed edits to existing rows → [diff-table](https://beamline.io/components/diff-table).
- Editing records after import → [data-grid](https://beamline.io/components/data-grid).
- Mapping value lists (Hot → Negotiation) or building transforms → the application's own form.

## Anatomy

- header: title, source line (file · rows · columns → destination), steps (Map columns / Preview / Import)
- stage heading (focus target) and a polite status region
- map: toolbar (filter with counts, search, bulk Accept suggestions / Don't import undecided, check status)
- map: summary of blockers with links (after a blocked Preview)
- map: pairs in file order — source (grip, name, path, samples), seam, chooser, suggestion with Accept, interpretation, check, invalid-value policy, transfer notice with Undo
- map: destinations panel in destination order (grouped by path), each field mapped / not mapped / required; drop targets
- preview: table of sample rows in destination order (field, from-column lineage, Change), converted cells, invalid cells with reasons; scope line
- import: progress (measured when reported), Cancel; result counts, outcomes list (failed first), lifetime note
- footer: Back · Preview / Import N rows / Retry N failed rows · View records · Done

## Variants

- **frame**: surface, none
- **layout**: auto, stacked

## States

- map: undecided, suggested, mapped, skipped (Don't import), transferred (notice + Undo)
- check: checking (stale revision), ok, invalid (count, examples, policy), ambiguous (interpretation needed), assessment error (Retry)
- blocked: summary of what to settle before preview
- preview: read-only, rows that won't import and why
- import: working (progress, Cancel when supported), request rejected (back to preview, draft kept), result, partial failure (retry failed rows only), retrying, cancelled (remaining rows offered)
- disabled, empty (no columns)

## Keyboard

- Tab moves through pairs in file order; each chooser keeps focus after Enter picks a field
- Chooser: ↓/↑ open and move, type to filter by name, path or type (several words match across them: “billing city”), Enter chooses, Esc closes and restores; nested fields sit under their path's heading. Below 640 px the chooser opens as a bottom sheet with its own search and focus returns to the field
- Accept on a suggestion moves focus to that pair's chooser
- Interpretation and invalid-value policy are radio groups: arrows choose
- Undo on a transfer notice restores both pairs and returns focus to the pair being edited
- Destinations panel is one tab stop: ↑/↓, Home/End, type-ahead; Enter on a mapped field focuses its pair, on an unmapped field opens a source picker
- Preview: Change on a column returns to that pair; Back returns to the Preview button with the list scrolled as it was
- Every stage change moves focus to the stage heading

## Motion

Choosing never moves anything; notices and the blocker summary open with the disclosure height transition (in 90ms, out 180ms); stage bodies fade in 90ms under an anchored header; drag shows a ghost chip that fades out on cancel. Reduced motion: same content and focus, all transitions instant.

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `source` (required) | `ImportSource` | — | File ID, name, row count, columns in file order and sample rows, as the application read them. |
| `destination` (required) | `ImportDestination` | — | Record type ID, name and fields in destination order (type, required, path). |
| `value / defaultValue / onValueChange` | `ImportMappingDraft` | — | The mapping draft keyed by source column ID, with a revision that increments on every change. |
| `suggestions` | `Record<sourceColumnId, ImportSuggestion>` | — | Hints with a reason; never accepted automatically. |
| `assessment` | `ImportMappingAssessment` | — | Caller-computed checks for one draft revision: per-pair conversions, interpretations, ready/blocked row counts. |
| `onAssessmentRetry` | `() => void` | — | Retry after a failed assessment. |
| `stage / defaultStage / onStageChange` | `"map" \| "preview" \| "import"` | — | Which stage is shown. |
| `onImport` | `(request, { onProgress, signal }) => Promise<ImportResult>` | — | Runs the import; reject only when nothing was written, otherwise resolve with an outcome per processed row. |
| `cancellable` | `boolean` | — | Show Cancel while importing; the operation honours the signal. |
| `result / defaultResult / onResultChange` | `ImportMappingResultState \| null` | — | Merged outcomes across attempts, so the caller can reopen a finished or running import. |
| `onBack` | `() => void` | — | Back from the Map stage to the caller's previous step. |
| `onOpenRecords / recordHref` | `(recordIds) => void / (recordId) => string` | — | Open the imported records; link each outcome's record. |
| `resultActions / onDone / lifetimeNote` | `ReactNode / () => void / ReactNode` | — | Extra result actions, the finishing action and how long the result lasts. |
| `frame` | `"surface" \| "none"` | — | Paint the root as a surface, or none inside a dialog, sheet or card. Default surface. |
| `layout` | `"auto" \| "stacked"` | — | Side-by-side destinations panel from 880px component width, or always stacked. Default auto. |
| `drag` | `boolean` | — | Pointer drag from a pair's grip onto a destination field; fine pointers only. Default true. |
| `title / emptyLabel / disabled / className / classNames` | `mixed` | — | Heading, empty text, read-only, root class and per-part classes keyed by part name. |

## Dependencies

`lucide-react`

## Import

```tsx
import { ImportMapping } from "@/components/import-mapping/import-mapping";
```

## Get it

Import mapping is part of Beamline: 190 React components and screens your coding agent (Claude Code, Codex, Cursor or any MCP client) installs into your app through Beamline's MCP server, as a ready-built package or as plain React source you can change. $49 one payment (regular $200), no subscription, a year of updates, one licence for your whole team. [Get Beamline](https://beamline.io/checkout?pack=complete) · [Connect your agent](https://beamline.io/connect)

## More in Data & tables

- [Data grid](https://beamline.io/components/data-grid): Virtualised grid for thousands of rows: multi-sort, search and value filters, resizable and pinnable columns, range selection, inline edit, footer…
- [Diff table](https://beamline.io/components/diff-table): Proposed edits to a table before they happen: new rows green, removals struck through, changed cells old → new; each change can be left out and Apply…
- [Filter toolbar](https://beamline.io/components/filter-toolbar): The filters on a list kept in view: chips that remove themselves, Add filter with fields and their values, Clear all, and room for search or view controls.
- [Table](https://beamline.io/components/table): A plain, styled HTML table for small static data: header, body, footer, caption, numeric cells, three densities and a sticky header.
