Skip to main content

Motivation Behind the Release

Refine v5 removes deprecated APIs and legacy systems, upgrades to TanStack Query v5, and adds React 19 support. The result is a cleaner, faster codebase with better developer experience.
Before upgrading to Refine 5, make sure that you are using Refine 4.x.x.If you are using Refine 3.x.x, refer to the migration guide for upgrading from 3.x.x to 4.x.x first.

Migration Overview

1

Upgrade the dependencies

Update all Refine packages to v5 and TanStack Query to v5
2

Remove deprecated APIs

Remove all deprecated APIs from Refine v4 using codemod or manually
3

Update router provider

Refactor legacy router provider to use new router provider
4

Update auth provider

Refactor legacy auth provider to use new auth provider
5

Upgrade TanStack Query

Follow TanStack Query migration guide for v4 to v5
6

(Optional) Upgrade React

Optionally upgrade React to v19

Step 1: Upgrade All Refine Dependencies to v5

Package Version Changes

All Refine packages have been bumped to the next major version as a coordinated release. This ensures maximum stability and compatibility when using packages together.
Packagev4 Versionv5 Version
@refinedev/core4.x.x5.x.x
react17 or 1818 or 19
@tanstack/react-query4.x.x5.x.x
@refinedev/antd5.x.x6.x.x
@refinedev/mui6.x.x7.x.x
@refinedev/mantine2.x.x3.x.x
@refinedev/chakra-ui2.x.x3.x.x
@refinedev/react-hook-form4.x.x5.x.x
@refinedev/react-table5.x.x6.x.x
@refinedev/react-router1.x.x2.x.x
@refinedev/nextjs-router6.x.x7.x.x
@refinedev/remix-router3.x.x4.x.x
@refinedev/inferencer5.x.x6.x.x
@refinedev/devtools1.x.x2.x.x

Update with Refine CLI

You can easily update Refine packages with the Refine CLI update command:
npm run refine update
React Query v5 is required for Refine v5. Make sure to install it when updating:
npm i @tanstack/react-query@5

Manual Update

Core packages (required):
npm i @refinedev/core@^5.0.0 @tanstack/react-query@^5.0.0
UI library packages (choose based on what you’re using):
# If using Ant Design
npm i @refinedev/antd@^6.0.0

# If using Material-UI
npm i @refinedev/mui@^7.0.0

# If using Mantine
npm i @refinedev/mantine@^3.0.0

# If using Chakra UI
npm i @refinedev/chakra-ui@^3.0.0
Router packages (choose based on your router):
# If using React Router
npm i @refinedev/react-router@^2.0.0

# If using Next.js
npm i @refinedev/nextjs-router@^7.0.0

# If using Remix
npm i @refinedev/remix-router@^4.0.0

Step 2: Remove All Deprecated APIs from Refine v4

All deprecated APIs marked for removal in v4 have been completely removed in v5. The @refinedev/codemod package handles the breaking changes for your project and automatically migrates it from 4.x.x to 5.x.x. Simply cd into the root folder of your project (where the package.json is located) and run this command:
npx @refinedev/codemod@latest refine4-to-refine5
Hook Return Type Changes: The codemod updates most standard cases, but may miss complex destructuring, conditional logic, or custom wrappers. Please review the Data & Mutation Hooks section if you use these patterns.

Changes Not Handled by the Codemod

Unfortunately, the codemod cannot cover every case. Below are changes that require manual updates:

useNavigation → useGo

Affects: Navigation helpers (push, replace, goBack)
The return values from useNavigation have been removed. You should now use useGo for navigation:
- import { useNavigation } from "@refinedev/core";
+ import { useGo } from "@refinedev/core";

- const { replace, push } = useNavigation();
- replace("/tasks/new");
+ const go = useGo();
+ go({ to: "/tasks/new", type: "replace" });
+ go({ to: "/tasks/new", type: "push" });
For backward navigation (goBack), use your router’s native API instead:
- import { useNavigation } from "@refinedev/core";
+ import { useNavigate } from "react-router";

- const { goBack } = useNavigation();
+ const navigate = useNavigate();
- goBack();
+ navigate(-1);

ITreeMenu → TreeMenuItem & list field changes

Affects: useMenu, custom sider renderers
  • ITreeMenu has been removed → use TreeMenuItem instead
  • list is now always a string route
  • list.path is gone and list is no longer a function
Why: Previously, you could define a React component in the <Refine /> resource as list. This is no longer supported. Routes/components must be defined in your router.
- const { menuItems, selectedKey } = useMenu();
- menuItems.map((item: ITreeMenu) => {
-   const { key, list } = item;
-   const route =
-     typeof list === "string"
-       ? list
-       : typeof list !== "function"
-       ? list?.path
-       : key;
- });
+ const { menuItems, selectedKey } = useMenu();
+ menuItems.map((item: TreeMenuItem) => {
+   const { list } = item;
+   const route = list ?? key; // always a string route now
+ });

Step 3: Update Router Provider

If your project is still using the legacyRouterProvider, you’ll need to migrate to the new router system. The new router provider offers greater flexibility and better integration with modern routing patterns.
<Refine
-    legacyRouterProvider={routerProvider}
+    routerProvider={routerProvider}
/>
Please refer to the Router Provider Migration Guide for complete migration instructions.

Step 4: Update Auth Provider

If your project is still using the legacy auth provider legacyAuthProvider or auth hooks with v3LegacyAuthProviderCompatible: true, you must migrate to the modern auth provider structure.
useLogin({
-    v3LegacyAuthProviderCompatible: true,
});

<Refine
-    legacyAuthProvider={legacyAuthProvider}
+    authProvider={authProvider}
/>
Please refer to the Auth Provider Migration Guide for complete migration instructions.

Step 5: Upgrade TanStack Query to v5

You’ll need to upgrade TanStack Query from v4 to v5. Please refer to the TanStack Query migration guide for detailed instructions on this upgrade.

Step 6: Upgrade React to v19 (Optional)

Refine v5 supports both React 18 and React 19. If you want to take advantage of the latest React features, you can optionally upgrade to React 19. Please refer to the React 19 release notes for more information.

Data & Mutation Hooks: Return Type Changes

Affects: All data and mutation hooks (useList, useTable, useInfiniteList, useOne, useMany, useForm, useCreate, useUpdate, etc.)
Return types of data and mutation hooks were refactored for clarity and consistency:
  • Query state (isLoading, isError, error, etc.) is now grouped under query object
  • Mutation state (isPending, isError, error, etc.) is now grouped under mutation object
  • Normalized values (data, total, etc.) are returned under a result object
This change:
  • Unifies the shape of return types across all hooks
  • Eliminates nested property access (e.g., data?.data)
  • Improves type safety and developer experience
  • Groups all TanStack Query APIs under the query object for easier discovery

useList

const {
-   data,
-   isLoading,
-   isError,
} = useList();

- const posts = data.data
- const total = data.total

const {
+   result,
+   query: { isLoading, isError },
} = useList();

+ const posts = result.data;
+ const total = result.total;

useOne, useMany, useShow

const {
-   data,
-   isLoading,
-   isError,
} = useOne({
    resource: "users",
    id: 1,
});

- const user = data.data;

const {
+   result,
+   query: { isLoading, isError },
} = useOne({
    resource: "users",
    id: 1,
});

+ const user = result;

useInfiniteList

const {
-  data,
-  isLoading,
-  isError,
-  fetchNextPage,
-  hasNextPage,
} = useInfiniteList({
  resource: "posts",
});

- const posts = data?.data;

const {
+  result,
+  query: { isLoading, isError, fetchNextPage, hasNextPage },
} = useInfiniteList({ resource: "posts" });

+ const posts = result.data;

Mutation Hooks

Affects: All mutation hooks (useUpdate, useDelete, useCreateMany, useUpdateMany, useDeleteMany, useCustomMutation)
const {
-  isPending,
-  isError,
   mutate,
   mutateAsync,
} = useUpdate({ resource: "posts" });

const {
+  mutation: { isPending, isError },
   mutate,
   mutateAsync,
} = useUpdate({ resource: "posts" });

List of All Breaking Changes

Affects: All data hooks, useForm, useTable, useDataGrid, useSelect, etc.
The metaData parameter has been renamed to meta across all hooks:
useList({
-    metaData: { foo: "bar" },
+    meta: { foo: "bar" },
})

useOne({
-    metaData: { headers: { "Authorization": "Bearer token" } },
+    meta: { headers: { "Authorization": "Bearer token" } },
})

useCreate({
-    metaData: { endpoint: "custom" },
+    meta: { endpoint: "custom" },
})
Affects: Type imports from @refinedev/core
Type interfaces have been renamed in @refinedev/core:
- import { type AuthBindings } from "@refinedev/core";
+ import { type AuthProvider  } from "@refinedev/core";
Affects: Type imports from @refinedev/core
Type interfaces have been renamed in @refinedev/core:
- import type { RouterBindings } from "@refinedev/core";
+ import type { RouterProvider  } from "@refinedev/core";
Affects: useList, useInfiniteList, useTable, useDataGrid, useSelect
The sorter and sort parameters have been renamed to sorters:
useList({
-    sort: [{ field: "title", order: "asc" }],
+    sorters: [{ field: "title", order: "asc" }],
})

useTable({
-    initialSorter: [{ field: "createdAt", order: "desc" }],
+    sorters: {
+        initial: [{ field: "createdAt", order: "desc" }]
+    },
})
Affects: useList, useTable, useDataGrid, useSelect
Filter configuration has been simplified:
useList({
-    config: {
-        filters: [{ field: "status", operator: "eq", value: "published" }],
-    },
+    filters: [{ field: "status", operator: "eq", value: "published" }],
})

useTable({
-    initialFilter: [{ field: "category", operator: "eq", value: "tech" }],
-    permanentFilter: [{ field: "status", operator: "eq", value: "active" }],
+    filters: {
+        initial: [{ field: "category", operator: "eq", value: "tech" }],
+        permanent: [{ field: "status", operator: "eq", value: "active" }]
+    },
})
Affects: useList, useTable, useDataGrid, useSelect
Pagination configuration has been restructured:
useList({
-    hasPagination: false,
+    pagination: { mode: "off" },
})

useTable({
-    initialCurrent: 1,
-    initialPageSize: 20,
-    hasPagination: false,
+    pagination: { mode: "off", currentPage: 1, pageSize: 20 },
})
Affects: useTable, useDataGrid, useSimpleList, useSubscription, useList, useCheckboxGroup, useSelect
useTable({
-   pagination: { current: 1 },
+   pagination: { currentPage: 1 },
})
Affects: useTable, useDataGrid, useSimpleList
const {
-    setCurrent,
-    current,
+    currentPage,
+    setCurrentPage,
} = useTable();
Affects: Resource definitions in <Refine> component
<Refine
    resources={[
        {
            name: "posts",
-            options: { label: "Blog Posts" },
+            meta: { label: "Blog Posts" },
        },
    ]}
/>
Affects: useImport, useExport, all Button components
useImport({
-    resourceName: "posts",
+    resource: "posts",
})

<CreateButton
-    resourceNameOrRouteName="posts"
+    resource="posts"
/>
Affects: useForm, useSelect, useShow, useSimpleList, useMany
const {
-    queryResult,
+    query,
} = useShow();

const {
-    queryResult,
+    query,
} = useForm();
Affects: useTable, useDataGrid
const {
-    tableQueryResult,
+    tableQuery,
} = useTable();
Affects: useCreate, useUpdate, useDelete, useCreateMany, useUpdateMany, useDeleteMany, useCustomMutation
const {
-    mutationResult,
+    mutation,
} = useForm();
The useResource hook has been removed in favor of useResourceParams:
- import { useResource } from "@refinedev/core";
+ import { useResourceParams } from "@refinedev/core";

- useResource("posts");
+ useResourceParams({ resource: "posts" });
<CreateButton
-    ignoreAccessControlProvider
+    accessControl={{ enabled: false }}
-    resourceNameOrRouteName="posts"
+    resource="posts"
/>
Affects: Layout components across all UI packages
The V2 layout components have been renamed to remove the V2 suffix:
  • ThemedLayoutV2ThemedLayout
  • ThemedTitleV2ThemedTitle
  • ThemedSiderV2ThemedSider
  • ThemedHeaderV2ThemedHeader
- import { ThemedLayoutV2, ThemedTitleV2 } from "@refinedev/antd";
+ import { ThemedLayout, ThemedTitle } from "@refinedev/antd";
export const dataProvider = {
    getList: ({
        resource,
        pagination: {
+            mode: "off" | "server" | "client",
        },
-        hasPagination,
+        sorters,
-        sort,
        filters,
+        meta,
-        metaData,
    }) => {
        // ...
    },

    custom: ({
        // ...
+        sorters,
-        sort,
    }) => {
        // ...
    },
};
useImport({
-    resourceName: "posts",
+    resource: "posts",
-    metaData: { foo: "bar" },
+    meta: { foo: "bar" },
})

useExport({
-    resourceName: "posts",
+    resource: "posts",
-    sorter: [{ field: "title", order: "asc" }],
+    sorters: [{ field: "title", order: "asc" }],
-    metaData: { foo: "bar" },
+    meta: { foo: "bar" },
-    exportOptions: {},
+    unparseConfig: {},
})
Affects: Custom implementations using Refine helpers
- import { queryKeys } from "@refinedev/core";
+ import { keys } from "@refinedev/core";

- queryKeys.data().resource("posts").action("list").get();
+ keys().data().resource("posts").action("list").get();

Build docs developers (and LLMs) love