useImport is a React hook that handles CSV file imports. It parses CSV files and creates records in your data provider with support for batch processing and progress tracking.
Usage
import { useImport } from "@refinedev/core";
const PostList = () => {
const { inputProps, isLoading } = useImport();
return (
<div>
<input {...inputProps} disabled={isLoading} />
{isLoading && <p>Importing...</p>}
</div>
);
};
Parameters
Return Values
Props to spread on an HTML file input element.onChange
(event: React.ChangeEvent<HTMLInputElement>) => void
Change handler for the file input.
mutationResult
UseCreateReturnType | UseCreateManyReturnType
The mutation result from either useCreate or useCreateMany, depending on batchSize.
Loading state indicator. true while parsing and importing data.
handleChange
HandleChangeType<TVariables, TData>
Manual handler function for file changes. Use this if you need custom file input handling.(params: { file: Partial<File> }) => Promise<CreatedValuesType<TVariables, TData>[]>
Examples
Basic Import
import { useImport } from "@refinedev/core";
const PostImport = () => {
const { inputProps, isLoading } = useImport();
return (
<div>
<label>
Import CSV:
<input {...inputProps} disabled={isLoading} />
</label>
{isLoading && <p>Importing data...</p>}
</div>
);
};
Import with Data Mapping
import { useImport } from "@refinedev/core";
const UserImport = () => {
const { inputProps } = useImport({
mapData: (item) => ({
name: item.Name,
email: item.Email,
role: item.Role || "user",
active: item.Status === "Active",
}),
});
return <input {...inputProps} />;
};
Import with Batch Size
import { useImport } from "@refinedev/core";
const BulkImport = () => {
const { inputProps, isLoading } = useImport({
batchSize: 50, // Process 50 records at a time
});
return (
<div>
<input {...inputProps} />
{isLoading && <p>Processing in batches of 50...</p>}
</div>
);
};
Import with Progress Tracking
import { useImport } from "@refinedev/core";
import { useState } from "react";
const ImportWithProgress = () => {
const [progress, setProgress] = useState(0);
const { inputProps, isLoading } = useImport({
onProgress: ({ totalAmount, processedAmount }) => {
const percentage = (processedAmount / totalAmount) * 100;
setProgress(percentage);
},
});
return (
<div>
<input {...inputProps} />
{isLoading && (
<div>
<p>Importing... {progress.toFixed(0)}%</p>
<progress value={progress} max={100} />
</div>
)}
</div>
);
};
Import with Completion Handler
import { useImport, useNotification } from "@refinedev/core";
const ImportWithNotification = () => {
const { open } = useNotification();
const { inputProps } = useImport({
onFinish: (results) => {
const successCount = results.succeeded.length;
const errorCount = results.errored.length;
open?.({
type: successCount > 0 ? "success" : "error",
message: "Import Complete",
description: `Successfully imported ${successCount} records. ${errorCount} errors.`,
});
},
});
return <input {...inputProps} />;
};
import { useImport } from "@refinedev/core";
import { useRef } from "react";
const CustomImportButton = () => {
const { inputProps, isLoading } = useImport();
const fileInputRef = useRef<HTMLInputElement>(null);
return (
<div>
<input
{...inputProps}
ref={fileInputRef}
style={{ display: "none" }}
/>
<button
onClick={() => fileInputRef.current?.click()}
disabled={isLoading}
>
{isLoading ? "Importing..." : "Import CSV"}
</button>
</div>
);
};
Import with Custom CSV Parsing
import { useImport } from "@refinedev/core";
const CustomParseImport = () => {
const { inputProps } = useImport({
paparseOptions: {
header: true,
skipEmptyLines: true,
delimiter: ";",
encoding: "UTF-8",
},
});
return <input {...inputProps} />;
};
Import with One-by-One Processing
import { useImport } from "@refinedev/core";
const SequentialImport = () => {
const { inputProps, isLoading } = useImport({
batchSize: 1, // Process one record at a time
});
return (
<div>
<input {...inputProps} />
{isLoading && <p>Processing records sequentially...</p>}
</div>
);
};
Manual File Handling
import { useImport } from "@refinedev/core";
const ManualImport = () => {
const { handleChange, isLoading } = useImport();
const onFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
const results = await handleChange({ file });
console.log("Import results:", results);
}
};
return (
<input
type="file"
accept=".csv"
onChange={onFileSelect}
disabled={isLoading}
/>
);
};
Advanced Import with Error Handling
import { useImport, useNotification } from "@refinedev/core";
import { useState } from "react";
const AdvancedImport = () => {
const { open } = useNotification();
const [errors, setErrors] = useState([]);
const { inputProps, isLoading } = useImport({
batchSize: 100,
mapData: (item) => ({
title: item.Title,
content: item.Content,
status: item.Status || "draft",
}),
onFinish: (results) => {
if (results.errored.length > 0) {
setErrors(results.errored);
open?.({
type: "error",
message: "Import Errors",
description: `${results.errored.length} records failed to import.`,
});
} else {
open?.({
type: "success",
message: "Import Successful",
description: `${results.succeeded.length} records imported successfully.`,
});
}
},
});
return (
<div>
<input {...inputProps} />
{isLoading && <p>Importing...</p>}
{errors.length > 0 && (
<div>
<h3>Import Errors:</h3>
<ul>
{errors.map((error, index) => (
<li key={index}>{JSON.stringify(error.response)}</li>
))}
</ul>
</div>
)}
</div>
);
};
The CSV file should have headers in the first row. Example:
API Reference
Type definitions:
export type MapDataFn<TItem, TVariables> = (
item: TItem,
index?: number,
items?: TItem[],
) => TVariables;
export type OnFinishParams<TVariables, TData> = {
succeeded: ImportSuccessResult<TVariables, TData>[];
errored: ImportErrorResult<TVariables>[];
};
export type OnProgressParams = {
totalAmount: number;
processedAmount: number;
};
export type UseImportInputPropsType = {
type: "file";
accept: string;
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
};
If batchSize is greater than 1, your data provider must implement the createMany method. If it’s set to 1, only the create method is required.
Use the mapData function to validate and transform CSV data before creating records. This is the perfect place to set default values, format dates, or validate required fields.
Large CSV files with small batch sizes may result in many API requests. Consider using larger batch sizes for better performance, but ensure your API can handle batch operations.