Skip to main content
Refine is built on a modular, provider-based architecture that separates concerns and enables maximum flexibility. At its core, Refine acts as a coordination layer between your UI components and various backend services through a well-defined provider system.

Core Architecture

Refine’s architecture is composed of several key layers:

Component Hierarchy

The <Refine> component is the root of your application and initializes all provider contexts:
import { Refine } from "@refinedev/core";
import { dataProvider } from "./providers/data";
import { authProvider } from "./providers/auth";

function App() {
  return (
    <Refine
      dataProvider={dataProvider}
      authProvider={authProvider}
      resources={[
        {
          name: "posts",
          list: "/posts",
          create: "/posts/create",
          edit: "/posts/edit/:id",
          show: "/posts/show/:id",
        },
      ]}
      options={{
        syncWithLocation: true,
        warnWhenUnsavedChanges: true,
      }}
    >
      {/* Your app routes and components */}
    </Refine>
  );
}
The <Refine> component creates a nested context provider structure:
// Simplified internal structure from packages/core/src/components/containers/refine/index.tsx
<QueryClientProvider>
  <NotificationContextProvider>
    <AuthProviderContextProvider>
      <DataContextProvider>
        <LiveContextProvider>
          <RouterContextProvider>
            <ResourceContextProvider>
              <I18nContextProvider>
                <AccessControlContextProvider>
                  <AuditLogContextProvider>
                    <UndoableQueueContextProvider>
                      <RefineContextProvider>
                        <UnsavedWarnContextProvider>
                          {children}
                        </UnsavedWarnContextProvider>
                      </RefineContextProvider>
                    </UndoableQueueContextProvider>
                  </AuditLogContextProvider>
                </AccessControlContextProvider>
              </I18nContextProvider>
            </ResourceContextProvider>
          </RouterContextProvider>
        </LiveContextProvider>
      </DataContextProvider>
    </AuthProviderContextProvider>
  </NotificationContextProvider>
</QueryClientProvider>
This nested structure ensures that all Refine hooks have access to the necessary contexts throughout your application.

Provider System

Providers are the core abstraction in Refine. Each provider implements a specific interface that Refine uses to interact with external services:

Provider Types

ProviderPurposeRequired
Data ProviderCRUD operations and API communicationYes
Auth ProviderAuthentication and authorizationNo
Router ProviderRouting and navigationNo
Access Control ProviderPermission-based access controlNo
Notification ProviderUser notificationsNo
i18n ProviderInternationalizationNo
Live ProviderReal-time updatesNo
Audit Log ProviderChange tracking and loggingNo
Only the Data Provider is required. All other providers are optional and add specific capabilities to your application.

React Query Integration

Refine leverages TanStack Query (React Query) for all data management:
  • Query management: All data fetching hooks (useList, useOne, etc.) return React Query results
  • Caching: Automatic caching with intelligent cache key generation
  • Optimistic updates: Built-in support for optimistic UI updates
  • Background refetching: Automatic data synchronization
  • Invalidation: Smart query invalidation on mutations
import { useList } from "@refinedev/core";

function PostList() {
  const { query, result } = useList({
    resource: "posts",
    pagination: { current: 1, pageSize: 10 },
  });

  // query is a React Query result with isLoading, error, etc.
  // result contains { data: Post[], total: number }
  
  return (
    <div>
      {query.isLoading && <div>Loading...</div>}
      {result.data.map((post) => (
        <div key={post.id}>{post.title}</div>
      ))}
    </div>
  );
}

Hooks Layer

Refine provides a comprehensive set of hooks that abstract common operations:

Data Hooks

  • useList - Fetch lists with pagination, filtering, sorting
  • useOne - Fetch single records
  • useCreate - Create new records
  • useUpdate - Update existing records
  • useDelete - Delete records
  • useCustom - Custom API calls

Auth Hooks

  • useLogin - Handle login
  • useLogout - Handle logout
  • useIsAuthenticated - Check authentication status
  • useGetIdentity - Get current user identity
  • usePermissions - Get user permissions

UI Hooks

  • useTable - Table state management
  • useForm - Form state management
  • useModal - Modal state management
  • useDrawer - Drawer state management
  • useGo - Programmatic navigation
  • useBack - Navigate back
  • useParsed - Parse current route
  • useResource - Get current resource

Mutation Modes

Refine supports three mutation modes that affect how UI updates are handled:
<Refine
  options={{
    mutationMode: "optimistic", // or "pessimistic" or "undoable"
  }}
>

Pessimistic (Default)

Waits for the server response before updating the UI:

Optimistic

Updates UI immediately, rolls back on error:

Undoable

Updates UI immediately with undo option:
Use optimistic mode for better perceived performance, undoable for operations that users might want to reverse, and pessimistic when you need guaranteed server confirmation.

Resource System

Resources represent the entities in your application (posts, users, products, etc.). They define:
  • Name: Identifier used in API calls
  • Routes: Paths for list, create, edit, show pages
  • Meta: Additional configuration (labels, icons, permissions, etc.)
resources={[
  {
    name: "posts",
    list: "/posts",
    create: "/posts/create",
    edit: "/posts/edit/:id",
    show: "/posts/show/:id",
    meta: {
      label: "Blog Posts",
      icon: <PostIcon />,
      canDelete: true,
    },
  },
]}

Type Safety

Refine is built with TypeScript and provides full type inference:
import { BaseRecord, useList } from "@refinedev/core";

interface IPost extends BaseRecord {
  id: number;
  title: string;
  content: string;
  status: "draft" | "published";
}

function PostList() {
  // result.data is typed as IPost[]
  const { result } = useList<IPost>({
    resource: "posts",
  });

  return result.data.map((post) => (
    <div key={post.id}>
      <h2>{post.title}</h2> {/* TypeScript knows about title */}
      <p>{post.status}</p> {/* Autocomplete for "draft" | "published" */}
    </div>
  ));
}

Extension Points

Refine is designed to be extended:
  1. Custom Providers: Implement provider interfaces for any backend
  2. Custom Hooks: Build on top of Refine’s primitives
  3. Meta Fields: Pass custom data through the meta object
  4. Custom Queries: Use useCustom for non-CRUD operations
// Custom hook example
import { useCustom } from "@refinedev/core";

function useStatistics() {
  return useCustom({
    url: `${API_URL}/statistics`,
    method: "get",
  });
}

Next Steps

Providers

Learn about the provider system in detail

Resources

Understand how resources work

Data Fetching

Deep dive into data management

Routing

Explore the routing system

Build docs developers (and LLMs) love