@refinedev/chakra-ui package offers pre-built components that integrate seamlessly with Refine’s core functionality while maintaining Chakra’s focus on accessibility.
Installation
npm install @refinedev/chakra-ui @chakra-ui/react @emotion/react @emotion/styled framer-motion @tabler/icons-react dayjs
Setup
Basic Setup
Wrap your application with Chakra UI’s provider:App.tsx
import { Refine } from "@refinedev/core";
import {
ThemedLayout,
RefineThemes,
useNotificationProvider,
} from "@refinedev/chakra-ui";
import { ChakraProvider } from "@chakra-ui/react";
import dataProvider from "@refinedev/simple-rest";
function App() {
return (
<ChakraProvider theme={RefineThemes.Blue}>
<Refine
dataProvider={dataProvider("https://api.example.com")}
notificationProvider={useNotificationProvider}
resources={[
{
name: "products",
list: "/products",
create: "/products/create",
edit: "/products/edit/:id",
show: "/products/show/:id",
},
]}
>
<ThemedLayout>
{/* Your pages here */}
</ThemedLayout>
</Refine>
</ChakraProvider>
);
}
export default App;
Available Themes
Refine provides pre-configured Chakra UI themes:import { RefineThemes } from "@refinedev/chakra-ui";
// Available themes:
RefineThemes.Blue
RefineThemes.Purple
RefineThemes.Magenta
RefineThemes.Red
RefineThemes.Orange
RefineThemes.Yellow
Custom Theme
Customize the theme using Chakra UI’sextendTheme:
import { ChakraProvider, extendTheme } from "@chakra-ui/react";
const customTheme = extendTheme({
colors: {
brand: {
50: "#e3f2fd",
100: "#bbdefb",
500: "#2196f3",
900: "#0d47a1",
},
},
fonts: {
heading: "Inter, sans-serif",
body: "Inter, sans-serif",
},
config: {
initialColorMode: "light",
useSystemColorMode: true,
},
});
function App() {
return (
<ChakraProvider theme={customTheme}>
{/* Your app */}
</ChakraProvider>
);
}
Layout Components
ThemedLayout
The main layout component with sidebar, header, and content:import { ThemedLayout } from "@refinedev/chakra-ui";
function App() {
return (
<Refine
// ... other props
>
<ThemedLayout>
{/* Your routes */}
</ThemedLayout>
</Refine>
);
}
Custom Layout Components
import {
ThemedLayout,
ThemedSider,
ThemedHeader,
ThemedTitle,
} from "@refinedev/chakra-ui";
function App() {
return (
<ThemedLayout
Sider={() => <ThemedSider />}
Header={() => <ThemedHeader sticky />}
Title={({ collapsed }) => (
<ThemedTitle collapsed={collapsed} text="My Admin" />
)}
>
{/* Your routes */}
</ThemedLayout>
);
}
CRUD Components
List
Display list with Chakra UI Table:pages/products/list.tsx
import {
List,
EditButton,
ShowButton,
DeleteButton,
} from "@refinedev/chakra-ui";
import { useTable } from "@refinedev/react-table";
import { ColumnDef, flexRender } from "@tanstack/react-table";
import {
Table,
Thead,
Tbody,
Tr,
Th,
Td,
HStack,
Button,
} from "@chakra-ui/react";
import { useMemo } from "react";
export const ProductList = () => {
const columns = useMemo<ColumnDef<any>[]>(
() => [
{
id: "id",
accessorKey: "id",
header: "ID",
},
{
id: "name",
accessorKey: "name",
header: "Name",
},
{
id: "price",
accessorKey: "price",
header: "Price",
},
{
id: "actions",
header: "Actions",
cell: ({ row }) => (
<HStack spacing={2}>
<EditButton hideText size="sm" recordItemId={row.original.id} />
<ShowButton hideText size="sm" recordItemId={row.original.id} />
<DeleteButton hideText size="sm" recordItemId={row.original.id} />
</HStack>
),
},
],
[],
);
const { getHeaderGroups, getRowModel } = useTable({
columns,
});
return (
<List>
<Table variant="simple">
<Thead>
{getHeaderGroups().map((headerGroup) => (
<Tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<Th key={header.id}>
{flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</Th>
))}
</Tr>
))}
</Thead>
<Tbody>
{getRowModel().rows.map((row) => (
<Tr key={row.id}>
{row.getVisibleCells().map((cell) => (
<Td key={cell.id}>
{flexRender(cell.column.columnDef.cell, cell.getContext())}
</Td>
))}
</Tr>
))}
</Tbody>
</Table>
</List>
);
};
Create
Create form with Chakra UI components:pages/products/create.tsx
import { Create } from "@refinedev/chakra-ui";
import { useForm } from "@refinedev/react-hook-form";
import {
FormControl,
FormLabel,
FormErrorMessage,
Input,
Textarea,
} from "@chakra-ui/react";
export const ProductCreate = () => {
const {
refineCore: { formLoading },
saveButtonProps,
register,
formState: { errors },
} = useForm();
return (
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
<FormControl mb="3" isInvalid={!!errors?.name}>
<FormLabel>Name</FormLabel>
<Input
type="text"
{...register("name", {
required: "Name is required",
})}
/>
<FormErrorMessage>{`${errors.name?.message}`}</FormErrorMessage>
</FormControl>
<FormControl mb="3" isInvalid={!!errors?.price}>
<FormLabel>Price</FormLabel>
<Input
type="number"
step="0.01"
{...register("price", {
required: "Price is required",
})}
/>
<FormErrorMessage>{`${errors.price?.message}`}</FormErrorMessage>
</FormControl>
<FormControl mb="3">
<FormLabel>Description</FormLabel>
<Textarea {...register("description")} />
</FormControl>
</Create>
);
};
Edit
Edit form with automatic data fetching:pages/products/edit.tsx
import { Edit } from "@refinedev/chakra-ui";
import { useForm } from "@refinedev/react-hook-form";
import {
FormControl,
FormLabel,
FormErrorMessage,
Input,
} from "@chakra-ui/react";
export const ProductEdit = () => {
const {
refineCore: { queryResult, formLoading },
saveButtonProps,
register,
formState: { errors },
} = useForm();
return (
<Edit isLoading={formLoading} saveButtonProps={saveButtonProps}>
<FormControl mb="3">
<FormLabel>ID</FormLabel>
<Input type="text" {...register("id")} disabled />
</FormControl>
<FormControl mb="3" isInvalid={!!errors?.name}>
<FormLabel>Name</FormLabel>
<Input
type="text"
{...register("name", {
required: "Name is required",
})}
/>
<FormErrorMessage>{`${errors.name?.message}`}</FormErrorMessage>
</FormControl>
<FormControl mb="3" isInvalid={!!errors?.price}>
<FormLabel>Price</FormLabel>
<Input
type="number"
step="0.01"
{...register("price", {
required: "Price is required",
})}
/>
<FormErrorMessage>{`${errors.price?.message}`}</FormErrorMessage>
</FormControl>
</Edit>
);
};
Show
Display single record:pages/products/show.tsx
import {
Show,
TextField,
NumberField,
DateField,
} from "@refinedev/chakra-ui";
import { useShow } from "@refinedev/core";
import { Heading } from "@chakra-ui/react";
export const ProductShow = () => {
const { queryResult } = useShow();
const { data, isLoading } = queryResult;
const record = data?.data;
return (
<Show isLoading={isLoading}>
<Heading as="h5" size="sm" mt={4}>
ID
</Heading>
<TextField value={record?.id} />
<Heading as="h5" size="sm" mt={4}>
Name
</Heading>
<TextField value={record?.name} />
<Heading as="h5" size="sm" mt={4}>
Price
</Heading>
<NumberField
value={record?.price}
options={{
style: "currency",
currency: "USD",
}}
/>
<Heading as="h5" size="sm" mt={4}>
Created At
</Heading>
<DateField value={record?.createdAt} />
</Show>
);
};
Field Components
Chakra UI field components for displaying data:import {
TextField,
NumberField,
DateField,
EmailField,
UrlField,
BooleanField,
TagField,
MarkdownField,
} from "@refinedev/chakra-ui";
import { VStack } from "@chakra-ui/react";
function FieldExamples() {
return (
<VStack spacing={3} align="stretch">
<TextField value="Simple text" />
<NumberField
value={1234.56}
options={{ style: "currency", currency: "USD" }}
/>
<DateField value="2024-01-15" format="LL" />
<EmailField value="[email protected]" />
<UrlField value="https://example.com" />
<BooleanField value={true} />
<TagField value="Active" colorScheme="green" />
<MarkdownField value="# Hello\n\nThis is **markdown**" />
</VStack>
);
}
Button Components
Chakra UI styled action buttons:import {
CreateButton,
EditButton,
ShowButton,
DeleteButton,
ListButton,
RefreshButton,
SaveButton,
CloneButton,
ExportButton,
ImportButton,
} from "@refinedev/chakra-ui";
import { HStack } from "@chakra-ui/react";
function ButtonExamples() {
return (
<HStack spacing={2}>
<CreateButton resource="products" />
<EditButton resource="products" recordItemId="1" />
<ShowButton resource="products" recordItemId="1" />
<DeleteButton resource="products" recordItemId="1" />
<ListButton resource="products" />
<RefreshButton resource="products" />
<SaveButton />
<CloneButton resource="products" recordItemId="1" />
<ExportButton />
<ImportButton />
</HStack>
);
}
Advanced Features
Modal Forms
import { CreateButton, SaveButton } from "@refinedev/chakra-ui";
import { useForm } from "@refinedev/react-hook-form";
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalBody,
ModalFooter,
ModalCloseButton,
FormControl,
FormLabel,
Input,
useDisclosure,
} from "@chakra-ui/react";
export const ProductList = () => {
const { isOpen, onOpen, onClose } = useDisclosure();
const {
refineCore: { onFinish },
register,
handleSubmit,
} = useForm();
return (
<>
<CreateButton onClick={onOpen} />
<Modal isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalHeader>Create Product</ModalHeader>
<ModalCloseButton />
<ModalBody>
<FormControl>
<FormLabel>Name</FormLabel>
<Input {...register("name")} />
</FormControl>
</ModalBody>
<ModalFooter>
<SaveButton onClick={handleSubmit(onFinish)} />
</ModalFooter>
</ModalContent>
</Modal>
</>
);
};
Color Mode Toggle
import { IconButton, useColorMode } from "@chakra-ui/react";
import { IconSun, IconMoon } from "@tabler/icons-react";
function ColorModeToggle() {
const { colorMode, toggleColorMode } = useColorMode();
return (
<IconButton
aria-label="Toggle color mode"
icon={colorMode === "light" ? <IconMoon /> : <IconSun />}
onClick={toggleColorMode}
/>
);
}
Toast Notifications
import { Button, useToast } from "@chakra-ui/react";
function CustomNotification() {
const toast = useToast();
const handleClick = () => {
toast({
title: "Success",
description: "Operation completed successfully",
status: "success",
duration: 5000,
isClosable: true,
position: "top-right",
});
};
return <Button onClick={handleClick}>Show Notification</Button>;
}
Auto-Save Indicator
import { AutoSaveIndicator } from "@refinedev/chakra-ui";
import { useForm } from "@refinedev/react-hook-form";
import { FormControl, FormLabel, Input } from "@chakra-ui/react";
export const ProductEdit = () => {
const {
refineCore: { autoSaveProps },
register,
} = useForm({
refineCoreProps: {
autoSave: {
enabled: true,
debounce: 2000,
},
},
});
return (
<>
<AutoSaveIndicator {...autoSaveProps} />
<FormControl>
<FormLabel>Name</FormLabel>
<Input {...register("name")} />
</FormControl>
</>
);
};
Responsive Design
Chakra UI has excellent responsive utilities:import { Box, Grid, GridItem, useBreakpointValue } from "@chakra-ui/react";
function ResponsiveLayout() {
const columns = useBreakpointValue({ base: 1, md: 2, lg: 3 });
return (
<Grid
templateColumns={`repeat(${columns}, 1fr)`}
gap={6}
>
<GridItem>
{/* Content */}
</GridItem>
<GridItem>
{/* Content */}
</GridItem>
</Grid>
);
}
Accessibility Features
Chakra UI is built with accessibility in mind:import {
Button,
VisuallyHidden,
useColorModeValue,
} from "@chakra-ui/react";
function AccessibleButton() {
const bg = useColorModeValue("white", "gray.800");
return (
<Button
bg={bg}
aria-label="Save changes"
_hover={{ bg: "blue.500" }}
_focus={{
boxShadow: "outline",
outline: "none",
}}
>
<VisuallyHidden>Save changes</VisuallyHidden>
Save
</Button>
);
}
Style Props
Chakra UI’s style props make styling quick and easy:import { Box } from "@chakra-ui/react";
function StyledBox() {
return (
<Box
p={4}
m={2}
bg="blue.500"
color="white"
borderRadius="md"
boxShadow="lg"
_hover={{ bg: "blue.600" }}
_active={{ bg: "blue.700" }}
>
Styled Box
</Box>
);
}
Next Steps
Chakra UI Docs
Official Chakra UI documentation
Examples
See complete Chakra UI examples
Accessibility
Learn about accessibility features
Theming
Customize your theme