Skip to main content
useCustom is a modified version of TanStack Query’s useQuery for custom requests that don’t fit into the standard CRUD operations. It uses the custom method from the dataProvider.

Usage

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

interface IStatistics {
  totalSales: number;
  totalOrders: number;
  averageOrderValue: number;
}

const statistics = useCustom<IStatistics>({
  url: "https://api.example.com/statistics",
  method: "get",
});

Parameters

url
string
required
The URL endpoint for the custom request.
method
'get' | 'delete' | 'head' | 'options' | 'post' | 'put' | 'patch'
required
HTTP method for the request.
config
UseCustomConfig
Configuration for the request.
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.
queryOptions
UseQueryOptions
TanStack Query’s useQuery options.
successNotification
OpenNotificationParams | false | ((data, config) => OpenNotificationParams | false)
Success notification configuration. Set to false to disable.
errorNotification
OpenNotificationParams | false | ((error, config) => OpenNotificationParams | false)
Error notification configuration. Set to false to disable.
overtimeOptions
UseLoadingOvertimeOptionsProps
Configuration for loading overtime behavior.

Return Values

result
object
Simplified result object.
query
QueryObserverResult
TanStack Query’s useQuery return object.
overtime
object
Loading overtime information.

Examples

Basic GET Request

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

const { result, query } = useCustom({
  url: "https://api.example.com/statistics",
  method: "get",
});

const statistics = result.data;

POST Request with Payload

const { result } = useCustom({
  url: "https://api.example.com/calculate",
  method: "post",
  config: {
    payload: {
      values: [1, 2, 3, 4, 5],
      operation: "sum",
    },
  },
});

With Query Parameters

const { result } = useCustom({
  url: "https://api.example.com/search",
  method: "get",
  config: {
    query: {
      q: "refine",
      page: 1,
      limit: 10,
    },
  },
});

With Custom Headers

const { result } = useCustom({
  url: "https://api.example.com/data",
  method: "get",
  config: {
    headers: {
      "X-API-Key": "your-api-key",
      "Content-Type": "application/json",
    },
  },
});

With Filters and Sorters

const { result } = useCustom({
  url: "https://api.example.com/custom-list",
  method: "post",
  config: {
    filters: [
      {
        field: "status",
        operator: "eq",
        value: "active",
      },
    ],
    sorters: [
      {
        field: "createdAt",
        order: "desc",
      },
    ],
  },
});

Conditional Fetching

const [userId, setUserId] = useState<number>();

const { result } = useCustom({
  url: `https://api.example.com/users/${userId}/stats`,
  method: "get",
  queryOptions: {
    enabled: !!userId,
  },
});

With Data Transformation

const { result } = useCustom({
  url: "https://api.example.com/data",
  method: "get",
  queryOptions: {
    select: (data) => ({
      ...data,
      data: {
        ...data.data,
        formattedDate: new Date(data.data.timestamp).toLocaleString(),
      },
    }),
  },
});

Multiple Data Providers

const { result } = useCustom({
  url: "/analytics/report",
  method: "get",
  dataProviderName: "analytics-api",
});

Fetching External API

interface IGitHubUser {
  login: string;
  name: string;
  avatar_url: string;
  public_repos: number;
}

const { result } = useCustom<IGitHubUser>({
  url: "https://api.github.com/users/refinedev",
  method: "get",
  config: {
    headers: {
      Accept: "application/vnd.github.v3+json",
    },
  },
});

const user = result.data;

Aggregation or Analytics Query

interface ISalesAnalytics {
  totalRevenue: number;
  orderCount: number;
  topProducts: Array<{ id: number; name: string; sales: number }>;
}

const { result } = useCustom<ISalesAnalytics>({
  url: "https://api.example.com/analytics/sales",
  method: "post",
  config: {
    payload: {
      startDate: "2024-01-01",
      endDate: "2024-12-31",
      groupBy: "month",
    },
  },
});

GraphQL Query

const { result } = useCustom({
  url: "https://api.example.com/graphql",
  method: "post",
  config: {
    payload: {
      query: `
        query GetUser($id: ID!) {
          user(id: $id) {
            id
            name
            email
          }
        }
      `,
      variables: {
        id: "123",
      },
    },
  },
});

With Error Handling

const { result, query } = useCustom({
  url: "https://api.example.com/data",
  method: "get",
});

if (query.isLoading) {
  return <div>Loading...</div>;
}

if (query.isError) {
  return <div>Error: {query.error.message}</div>;
}

return <div>{JSON.stringify(result.data, null, 2)}</div>;

Manual Refetch

const { result, query } = useCustom({
  url: "https://api.example.com/data",
  method: "get",
});

const handleRefresh = () => {
  query.refetch();
};

return (
  <div>
    <button onClick={handleRefresh}>Refresh</button>
    <pre>{JSON.stringify(result.data, null, 2)}</pre>
  </div>
);

Type Parameters

  • TQueryFnData - Result data type returned by the query function. Extends BaseRecord.
  • TError - Custom error type that extends HttpError.
  • TQuery - Type of query parameters.
  • TPayload - Type of request payload/body.
  • TData - Result data type returned by the select function. Extends BaseRecord. Defaults to TQueryFnData.

FAQ

Use useCustom when:
  • You need to call a custom endpoint that doesn’t fit CRUD operations
  • You’re fetching aggregated data or analytics
  • You’re calling external/third-party APIs
  • You need custom request/response handling
GET requests typically don’t have a body. Use query for query parameters:
useCustom({
  url: "/api/data",
  method: "get",
  config: {
    query: { filter: "active" },
  },
});
For read operations, use useCustom. For mutations (POST/PUT/PATCH/DELETE that modify data), consider using useCustomMutation instead for better semantics and automatic invalidation support.
Yes, useCustom uses the custom method from your dataProvider. Make sure your dataProvider implements this method.
Authentication is typically handled by your dataProvider or HTTP client. You can also pass custom headers:
useCustom({
  url: "/api/data",
  method: "get",
  config: {
    headers: {
      Authorization: `Bearer ${token}`,
    },
  },
});

Build docs developers (and LLMs) love