Skip to main content
useCreateMany is a modified version of TanStack Query’s useMutation for multiple create mutations. It uses the createMany method from the dataProvider. If the dataProvider doesn’t have a createMany method, it will call create for each item individually.

Usage

import { useCreateMany } from "@refinedev/core";

interface IProduct {
  id: number;
  name: string;
  price: number;
}

const { mutate } = useCreateMany<IProduct>();

mutate({
  resource: "products",
  values: [
    { name: "Product 1", price: 99.99 },
    { name: "Product 2", price: 149.99 },
  ],
});

Parameters

You can pass parameters either to the hook or to the mutate/mutateAsync functions. Parameters passed to the mutation functions override those passed to the hook.
resource
string
Resource name for API data interactions.
values
TVariables[]
Array of values for the mutation function - the data to create.
meta
MetaQuery
Meta data for the dataProvider. Can be used to pass additional parameters to data provider methods.
dataProviderName
string
If there is more than one dataProvider, you should specify which one to use.
invalidates
Array<keyof IQueryKeys>
default:"['list', 'many']"
Specify which queries should be invalidated after successful mutation. Options: 'list', 'many', 'detail', 'resourceAll', 'all'.
successNotification
OpenNotificationParams | false | ((data, values) => OpenNotificationParams | false)
Success notification configuration. Set to false to disable.
errorNotification
OpenNotificationParams | false | ((error, values) => OpenNotificationParams | false)
Error notification configuration. Set to false to disable.
mutationOptions
UseMutationOptions
TanStack Query’s useMutation options.
overtimeOptions
UseLoadingOvertimeOptionsProps
Configuration for loading overtime behavior.

Return Values

mutate
(params?: UseCreateManyParams) => void
Function to trigger the mutation.
mutateAsync
(params?: UseCreateManyParams) => Promise<CreateManyResponse<TData>>
Async version that returns a promise.
mutation
UseMutationResult
TanStack Query’s useMutation return object.
overtime
object
Loading overtime information.

Examples

Basic Usage

import { useCreateMany } from "@refinedev/core";

const { mutate } = useCreateMany();

const handleSubmit = (values: IProduct[]) => {
  mutate({
    resource: "products",
    values,
  });
};

With Async/Await

const { mutateAsync } = useCreateMany();

const handleSubmit = async (values: IProduct[]) => {
  try {
    const result = await mutateAsync({
      resource: "products",
      values,
    });
    console.log("Created products:", result.data);
  } catch (error) {
    console.error("Failed to create products:", error);
  }
};

Pre-configured Hook

const { mutate } = useCreateMany({
  resource: "products",
});

// Now you only need to pass values
mutate({
  values: [
    { name: "Product 1", price: 99 },
    { name: "Product 2", price: 149 },
  ],
});

With Callbacks

const { mutate } = useCreateMany({
  mutationOptions: {
    onSuccess: (data) => {
      console.log("Products created:", data.data);
    },
    onError: (error) => {
      console.error("Error:", error.message);
    },
  },
});

mutate({
  resource: "products",
  values: [
    { name: "Product 1", price: 99 },
    { name: "Product 2", price: 149 },
  ],
});

Custom Invalidation

const { mutate } = useCreateMany();

mutate({
  resource: "products",
  values: [
    { name: "Product 1", price: 99 },
    { name: "Product 2", price: 149 },
  ],
  invalidates: ["list", "many", "detail"],
});

Disable Notifications

const { mutate } = useCreateMany({
  successNotification: false,
  errorNotification: false,
});

With Meta Data

const { mutate } = useCreateMany();

mutate({
  resource: "products",
  values: [
    { name: "Product 1", price: 99 },
    { name: "Product 2", price: 149 },
  ],
  meta: {
    headers: { "X-Custom-Header": "value" },
  },
});

Bulk Import

import { useCreateMany } from "@refinedev/core";
import { parse } from "papaparse";

const BulkImport = () => {
  const { mutate, mutation } = useCreateMany();

  const handleFileUpload = (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (!file) return;

    parse(file, {
      header: true,
      complete: (results) => {
        mutate({
          resource: "products",
          values: results.data,
        });
      },
    });
  };

  return (
    <div>
      <input type="file" accept=".csv" onChange={handleFileUpload} />
      {mutation.isPending && <p>Importing...</p>}
      {mutation.isSuccess && <p>Imported {mutation.data?.data.length} products</p>}
    </div>
  );
};

Type Parameters

  • TData - Result data type of the mutation. Extends BaseRecord.
  • TError - Custom error type that extends HttpError.
  • TVariables - Type of the values/payload for the mutation.

FAQ

If the dataProvider doesn’t implement the createMany method, Refine will automatically fall back to calling the create method for each item individually.
Access them from the data property:
const { mutate } = useCreateMany({
  mutationOptions: {
    onSuccess: (data) => {
      const ids = data.data.map(item => item.id);
      console.log("Created IDs:", ids);
    },
  },
});
Yes, set invalidates to an empty array:
mutate({
  resource: "products",
  values: [...],
  invalidates: [],
});

Build docs developers (and LLMs) love