Skip to main content

CSV Import

You can easily import CSV files for any resource by using refine's customizable useImport hook, optionally with <ImportButton> component. useImport hook returns the necessary props for <ImportButton> component. refine uses Papa Parse parser under the hood to parse CSV files.

You can call the useImport hook and add an <ImportButton> with properties returned from useImport on a list page, configured with a mapping function to format the files data into API's data. When the button gets triggered, it creates the imported resources using create or createMany dataProvider methods under the hood.

Usage

Let's look at an example of adding a custom import button:

pages/posts/list.tsx
import { useMany } from "@pankod/refine-core";
import {
List,
useTable,
useImport,
ImportButton,
} from "@pankod/refine-antd";

export const PostList: React.FC = () => {
const { tableProps } = useTable<IPost>();

const categoryIds =
tableProps?.dataSource?.map((item) => item.category.id) ?? [];
const { data, isLoading } = useMany<ICategory>({
resource: "categories",
ids: categoryIds,
queryOptions: {
enabled: categoryIds.length > 0,
},
});

const importProps = useImport<IPostFile>();

return (
<List
pageHeaderProps={{
extra: <ImportButton {...importProps} />,
}}
>
...
</List>
);
};

interface ICategory {
id: number;
title: string;
}

interface IPostFile {
id: number;
title: string;
content: string;
userId: number;
categoryId: number;
status: "published" | "draft" | "rejected";
}

interface IPost {
id: number;
title: string;
content: string;
status: "published" | "draft" | "rejected";
category: { id: number };
}
Import button

We should map CSV data into Post data. Assume that this is the CSV file content we have:

dummy.csv
"title","content","status","categoryId","userId"
"dummy title 1","dummy content 1","rejected","3","8"
"dummy title 2","dummy content 2","draft","44","8"
"dummy title 3","cummy content 3","published","41","10"

It has 3 entries. We should map categoryId to category.id and userId to user.id. Since these are objects, we store any relational data as their id in CSV.

This would make our useImport call look like this:

/src/pages/posts/list.tsx
export const PostList: React.FC = () => {
const { tableProps } = useTable<IPost>();

const categoryIds =
tableProps?.dataSource?.map((item) => item.category.id) ?? [];
const { data, isLoading } = useMany<ICategory>({
resource: "categories",
ids: categoryIds,
queryOptions: {
enabled: categoryIds.length > 0,
},
});

const importProps = useImport<IPostFile>({
mapData: (item) => {
return {
title: item.title,
content: item.content,
status: item.status,
category: {
id: item.categoryId,
},
user: {
id: item.userId,
},
};
},
});

return <></>;
};

And it's done. When you click on the button and provide a CSV file of the headers "title","content","status","categoryId" and "userId", it should be mapped and imported. Mapped data is the request payload. Either as part of an array or by itself as part of every request. In our example, it fires POST request/requests like this:

POST https://api.fake-rest.refine.dev/posts
{
"title": "dummy title 1",
"content": "dummy content 1",
"status": "rejected",
"category": {
"id": "3"
},
"user": {
"id": "8"
}
}
Importing CSV

Depending on the batchSize option, posts can get sent one by one or as batches. By default, all records are sent in one createMany call.

Live StackBlitz Example