DataImportWizard
Five-step spreadsheet import wizard (Upload → Map fields → Options → Preview → Result) in a full-height bottom sheet. The file is picked with the system document picker and parsed on the server. API-aligned with the web DataImportWizard.
Installation
pnpm dlx @docyrus/cli add @docyrus/rn-data-import-wizardpnpm add expo-document-picker (optional)expo-document-picker is an optional peer that is loaded lazily. Without it, the Choose file button is disabled and the upload step shows an install notice. You can still call onPickFile from your own picker.
DataImportWizard is the presentational shell: every piece of state comes in through props. To connect it to a Docyrus data source (upload → analyse → import endpoints, field schema, auto-mapping), use useDocyrusDataImportWizard, which wires every prop and returns a ready wizard element. Use the component directly only when you need your own state machine or back end.
Usage
import { useDocyrusClient } from '@docyrus/signin/react-native';
import { Button } from '@/components/docyrus-native/button';
import { useDocyrusDataImportWizard } from '@/hooks/docyrus-native/use-docyrus-data-import-wizard';
export function ImportContacts() {
const client = useDocyrusClient();
const { wizard, openWizard } = useDocyrusDataImportWizard({
client: client!,
appSlug: 'base',
dataSourceSlug: 'contact',
requiredFieldSlugs: ['name'],
onImported: result => console.log(result.totalSuccessfulRecords)
});
return (
<>
<Button onPress={openWizard}>Import</Button>
{wizard}
</>
);
}Driving the component yourself
import { DataImportWizard } from '@/components/docyrus-native/data-import-wizard';
<DataImportWizard
open={open}
onOpenChange={setOpen}
step={step}
onStepChange={setStep}
targetFields={fields}
requiredFieldSlugs={['name']}
uniqueFieldSlugs={['email']}
file={file}
uploadedFile={uploadedFile}
analysedFile={analysedFile}
importResult={importResult}
isUploading={false}
isAnalysing={false}
isImporting={false}
uploadError={null}
analyseError={null}
importError={null}
mapping={mapping}
onMappingChange={setMapping}
upsertUniqueFields={upsert}
onUpsertChange={setUpsert}
onPickFile={handlePickFile}
onAnalyse={handleAnalyse}
onConfirmMapping={() => setStep('options')}
onConfirmOptions={() => setStep('preview')}
onStartImport={handleImport}
onReset={handleReset}
onClose={() => setOpen(false)} />Wizard steps
| Step | Purpose |
|---|---|
| Upload | Opens the system document picker, filtered to the accepted MIME types. Checks the extension and size (empty files are rejected) before calling onPickFile. Continue (onAnalyse) retries the analyse phase. |
| Map fields | One card per source column with a target Select (data source fields, the reserved slugs marked System, or skip). Per type: relation reference_key, phone country code, date format, money / phone companion column. Required fields that are missing are listed. |
| Options | "Update existing records on unique-key match" switch (upsertUniqueFields) and summary tiles for rows, columns and unique fields. |
| Preview | The first previewRowCount rows in a horizontally scrolling table. Each cell renders through DocyValueDynamic, so it looks like the grid. |
| Result | Tiles for successful records, warnings, duplicates and errors, plus a per-row error list. Import another file (onReset) and Done (onClose). |
The footer shows Back, Cancel and the step's primary action. While uploading, analysing or importing, the sheet cannot be dismissed.
API Reference
DataImportWizardProps
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | — | Sheet visibility (required). |
onOpenChange | (open: boolean) => void | — | Visibility change handler (required). |
step | ImportWizardStep | — | Current step (required). |
onStepChange | (next: ImportWizardStep) => void | — | Step setter, used by Back (required). |
targetFields | ReadonlyArray<DocyrusFieldLike> | — | Fields the user can map columns onto (required). |
requiredFieldSlugs | ReadonlyArray<string> | [] | Slugs that must be mapped before leaving the mapping step. |
uniqueFieldSlugs | ReadonlyArray<string> | [] | Slugs covered by unique indexes (upsert toggle, uniqueData). |
file | DataImportFile | null | — | The picked file (required). |
uploadedFile | UploadedFileInfo | null | — | Upload response (required). |
analysedFile | AnalysedFile | null | — | Analyse response (required). |
importResult | ImportResultPayload | null | — | Import response (required). |
isUploading | boolean | — | Upload in flight (required). |
isAnalysing | boolean | — | Analyse in flight (required). |
isImporting | boolean | — | Import in flight (required). |
uploadError | Error | null | — | Upload error, shown in the upload step (required). |
analyseError | Error | null | — | Analyse error, shown in the upload step (required). |
importError | Error | null | — | Import error, shown in the result step (required). |
mapping | WizardMappingMap | — | Per-source-column mapping (required). |
onMappingChange | (next: WizardMappingMap) => void | — | Mapping setter (required). |
upsertUniqueFields | boolean | — | Upsert toggle value (required). |
onUpsertChange | (next: boolean) => void | — | Upsert toggle setter (required). |
onPickFile | (file: DataImportFile) => void | — | A valid file was picked (required). |
onAnalyse | () => void | — | Upload step primary action (required). |
onConfirmMapping | () => void | — | Mapping step primary action (required). |
onConfirmOptions | () => void | — | Options step primary action (required). |
onStartImport | () => void | — | Preview step primary action (required). |
onReset | () => void | — | "Import another file" / clear the picked file (required). |
onClose | () => void | — | Cancel / Done (required). |
title | ReactNode | t('ui.dataImportWizard.title', 'Import data') | Header title. Native renders strings only. |
description | ReactNode | t('ui.dataImportWizard.description', …) | Header description. Native renders strings only. |
icon | string | 'fal file-import' | DocyrusIcon next to the title. |
maxFileSizeBytes | number | 20 * 1024 * 1024 | Maximum file size (20 MB). |
acceptedExtensions | ReadonlyArray<string> | ['xlsx', 'xls', 'csv'] | Accepted file extensions. |
previewRowCount | number | 10 | Rows shown in the preview step. |
className | string | — | Extra classes for the sheet body. |
DataImportFile
DataImportFile is the native replacement for the DOM File: the NativeFile shape { uri: string; name: string; type: string; size?: number } returned by expo-document-picker and accepted by React Native FormData.
ColumnMappingState
| Field | Type | Description |
|---|---|---|
sourceColumn | string | Spreadsheet header. |
targetSlug | string | null | Target field slug, a reserved slug, or null to skip the column. |
companionSourceColumn | string | null | Money / phone companion column (currency, country). |
fieldOptions | { reference_key?: string; format?: string | ReadonlyArray<string> } | Relation reference key, phone default country code or date format tokens. |
Components
| Component | Description |
|---|---|
DataImportWizard | Sheet shell: AwesomeDialog + Stepper + step bodies + footer. |
UploadStep | Document picker, file card, upload / analyse spinners and errors. |
MappingStep | Per-column target select and per-type options. |
OptionsStep | Upsert switch and summary tiles. |
PreviewStep | Value-renderer preview table. |
ProgressStep | Import progress and result tiles. |
Each step is exported on its own so you can rearrange or replace steps in a custom flow.
Helpers
| Helper | Description |
|---|---|
buildAutoMapping(columns, fields) | Greedy slug + name + token-Jaccard matcher. Returns a WizardMappingMap. |
inferFieldOptions(field) | Default fieldOptions for relation, phone and date fields. |
validateMapping(mapping, requiredSlugs) | { valid, missing, duplicateTargets }. |
buildImportOptions(mapping, fields, upsert, uniqueData?) | UI state → import POST body. |
collectUniqueDataLookups(mapping, fields, analysedFile) | Columns that benefit from uniqueData pre-resolution. |
buildUniqueDataPayload(specs, resolved) | Builds the uniqueData payload entry. |
getSecondarySlug(fieldType) | '__amount_currency' / '__phone_country' for composite fields. |
isEnumLikeType(fieldType) / isRelationLikeType(fieldType) | Field-type predicates. |
slugify(value) / normalizeKey(value) | String helpers used by the auto-mapper. |
IMPORT_WIZARD_STEPS / RESERVED_TARGET_SLUGS | Step order and the reserved slugs (name, description, created_on, autonumber_id). |
Native differences
- The wizard is a full-height bottom-sheet
AwesomeDialogwith a compact, horizontally scrollingStepper. - There is no drag-and-drop. The file comes from the system document picker (optional
expo-document-picker). FilebecomesDataImportFile(NativeFile). No spreadsheet library runs on the device: the file is parsed by the analyse endpoint.title/descriptiononly render when they are strings.
Translation keys
ui.dataImportWizard.*: title, description, steps.upload | mapping | options | preview | result and actions.continue | uploading | analysing | mappingNext | optionsNext | startImport | importing | importingNow | back | cancel | importAnother | done, plus the per-step keys shared with web.
Type Exports
| Type | Description |
|---|---|
DataImportWizardProps | Component props. |
DataImportFile | NativeFile ({ uri, name, type, size? }). |
ImportWizardStep | 'upload' | 'mapping' | 'options' | 'preview' | 'progress' | 'result'. |
WizardMappingMap | Record<sourceColumn, ColumnMappingState>. |
ColumnMappingState | Per-column mapping state. |
UploadedFileInfo | Upload response: { fileName, originalName, size, mimeType, filePath }. |
AnalysedFile | Analyse response: { fileName, filePath?, columns, rows, uniqueFieldIds, uniqueFieldSlugs, dataSourceRecord? }. |
ImportPayloadOptions | Import POST body options. |
ImportResultPayload | Import response (success / warning counts, duplicates, errors). |
MappingValidationResult | { valid, missing, duplicateTargets }. |
ReservedTargetSlug | 'name' | 'description' | 'created_on' | 'autonumber_id'. |
UniqueDataLookupSpec | Produced by collectUniqueDataLookups. |
DataGridViewSelect
Saved-view switcher for a data grid — pill tabs, dropdown or picker sheet, active-view actions, a full-screen view editor with 11 sections and a manage-views sheet. Same API as the web component.
Data Table
Headless TanStack table renderer for React Native — FlashList body, sticky header, frozen columns, row grouping, skeleton loading and a pagination footer, with a data/columns convenience path.