xenial-black
xenial-black2h ago

useTable totals from backend

I return the totals from my backend, like for example: { data: [{...},{...},{...},{...},{...}] total: 5 } but I can't make it work with antd useTable, unless I parse const { result, tableProps } = useTable<MyType>({ with something like correctlyTypedResult = result as unknown as MyType
9 Replies
kapa.ai
kapa.ai2h ago
Hi @khadhulu. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Short answer: you shouldn’t need to cast result. Make sure your dataProvider’s getList returns the exact shape Refine expects, and type your useTable generics correctly. What useTable expects - The dataProvider.getList must return { data: TData[], total: number }. [useTable core] - Ant Design’s useTable is built on top of core useTable/useList and uses the same response shape. [Antd useTable; Invoice blog] Typical usage (no casts needed)
import { useTable, HttpError } from "@refinedev/antd";

interface MyType {
id: number;
// ...
}

const { result, tableProps } = useTable<MyType, HttpError>();

// result.data -> MyType[]
// result.total -> number
// <Table {...tableProps} rowKey="id" />
import { useTable, HttpError } from "@refinedev/antd";

interface MyType {
id: number;
// ...
}

const { result, tableProps } = useTable<MyType, HttpError>();

// result.data -> MyType[]
// result.total -> number
// <Table {...tableProps} rowKey="id" />
The migration guide clarifies that in v5 result now exposes data and total directly:
const { tableProps, result: { data, total } } = useTable();
const { tableProps, result: { data, total } } = useTable();
No TypeScript casting should be necessary if your provider returns { data: MyType[], total: number }. [Migration v4→v5] Common pitfalls - Returning a different shape from getList (e.g., totalCount instead of total). For GraphQL or custom backends, map your response to { data, total } in the data provider. [Issue 1802; Issue 1805] - Incorrect generics: useTable<TQueryFnData, TError, TSearchVariables, TData>. Usually you only need to set the record type (TQueryFnData/TData). [Type params] If you’re still forced to cast, double-check: - Your getList return signature is exactly { data: MyType[]; total: number }. [useTable core] - You’re importing useTable from @refinedev/antd and not mixing older API shapes. [Antd useTable] ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2h ago
<@236239059184386048> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
xenial-black
xenial-blackOP2h ago
I still have to get data from result.data.data @kapa.ai
kapa.ai
kapa.ai2h ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- In Refine v5, you should access list data as result.data (array) and result.total (number). If you’re seeing result.data.data, you’re likely on an older return shape or using older code. What to check: - You’re on the new v5 hook return shape and using the result object: - Correct: const { result } = useTable(); const rows = result.data; const total = result.total; - This is the documented v5 change. [Migration guide] - Your dataProvider.getList returns exactly { data: TData[]; total: number } (no nested data). [useTable core] - You’re importing useTable from @refinedev/antd and using its v5 API; it’s built on core useTable and exposes result with data and total. [Antd useTable] Quick example (v5):
import { useTable } from "@refinedev/antd";

type MyType = { id: number; name: string };

const { result, tableProps } = useTable<MyType>();

const rows = result?.data; // MyType[]
const total = result?.total; // number
import { useTable } from "@refinedev/antd";

type MyType = { id: number; name: string };

const { result, tableProps } = useTable<MyType>();

const rows = result?.data; // MyType[]
const total = result?.total; // number
This avoids result.data.data entirely. [Antd useTable; Migration guide] ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2h ago
<@236239059184386048> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
xenial-black
xenial-blackOP2h ago
@kapa.ai data isn't nested from my backend endpoint i return something like: { data: [{...},{...},{...},{...},{...}], total: 5 } but my dataSource still wraps it with data; thus making it { data: { data: [{...},{...},{...},{...},{...}] total: 5 }, total: 5}
kapa.ai
kapa.ai2h ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- This nesting happens when your dataProvider.getList returns an object that Refine then “normalizes” again, so the hook ends up seeing your whole payload as data, producing result = { data: { data: [...], total: 5 }, total: 5 } and thus tableProps.dataSource = result.data (the inner object), not the array. Ant Design’s useTable expects getList to return exactly { data: TData[]; total: number } with data being the array only. [useTable antd; useTable core; Migration v5] Fix: adjust your dataProvider.getList to map your API response to the expected shape. Example getList mapping:
getList: async ({ resource, pagination, filters, sorters, meta }) => {
const res = await httpClient.get(`/api/${resource}`);
// your API returns: { data: [...], total: 5 }
return {
data: res.data.data, // array only
total: res.data.total,
};
}
getList: async ({ resource, pagination, filters, sorters, meta }) => {
const res = await httpClient.get(`/api/${resource}`);
// your API returns: { data: [...], total: 5 }
return {
data: res.data.data, // array only
total: res.data.total,
};
}
Notes: - If you’re using a packaged provider (e.g., GraphQL) that assumes a different key like count, override its getList to return total as a number and data as an array. [Issue 2603] - With v5, consume the values via result.data and result.total, and pass tableProps to <Table />. No casts needed. [Migration v5; Antd intro] If you can’t change the provider globally, you can override the table’s dataSource temporarily:
const { result, tableProps } = useTable<MyType>();
<Table
{...tableProps}
rowKey="id"
dataSource={Array.isArray(result?.data) ? result.data : result?.data?.data}
/>
const { result, tableProps } = useTable<MyType>();
<Table
{...tableProps}
rowKey="id"
dataSource={Array.isArray(result?.data) ? result.data : result?.data?.data}
/>
But the recommended fix is to normalize getList. [Low level inspection] ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2h ago
<@236239059184386048> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
xenial-black
xenial-blackOP2h ago
imma crashout wtf

Did you find this page helpful?