@refinedev/mui package offers pre-built components that integrate seamlessly with Refine’s core functionality.
Installation
npm install @refinedev/mui @mui/material @mui/x-data-grid @mui/lab @mui/icons-material @emotion/react @emotion/styled dayjs
Setup
Basic Setup
Wrap your application with MUI’s theme provider:App.tsx
import { Refine } from "@refinedev/core";
import {
ThemedLayout,
RefineThemes,
useNotificationProvider,
RefineSnackbarProvider,
} from "@refinedev/mui";
import { CssBaseline, GlobalStyles, ThemeProvider } from "@mui/material";
import dataProvider from "@refinedev/simple-rest";
function App() {
return (
<ThemeProvider theme={RefineThemes.Blue}>
<CssBaseline />
<GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} />
<RefineSnackbarProvider>
<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>
</RefineSnackbarProvider>
</ThemeProvider>
);
}
export default App;
Available Themes
Refine provides pre-configured Material-UI themes:import { RefineThemes } from "@refinedev/mui";
// Available themes:
RefineThemes.Blue
RefineThemes.Purple
RefineThemes.Magenta
RefineThemes.Red
RefineThemes.Orange
RefineThemes.Yellow
RefineThemes.BlueDark // Dark mode variants
RefineThemes.PurpleDark
Custom Theme
Customize the theme using Material-UI’screateTheme:
import { createTheme, ThemeProvider } from "@mui/material";
const theme = createTheme({
palette: {
mode: "dark",
primary: {
main: "#1976d2",
},
secondary: {
main: "#dc004e",
},
},
typography: {
fontFamily: "Inter, sans-serif",
},
});
function App() {
return (
<ThemeProvider theme={theme}>
{/* Your app */}
</ThemeProvider>
);
}
Layout Components
ThemedLayout
The main layout component with sidebar, header, and content:import { ThemedLayout } from "@refinedev/mui";
function App() {
return (
<Refine
// ... other props
>
<ThemedLayout>
{/* Your routes */}
</ThemedLayout>
</Refine>
);
}
Custom Layout Components
import {
ThemedLayout,
ThemedSider,
ThemedHeader,
ThemedTitle,
} from "@refinedev/mui";
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 Material-UI DataGrid:pages/products/list.tsx
import { List, useDataGrid, EditButton, ShowButton } from "@refinedev/mui";
import { DataGrid, GridColDef } from "@mui/x-data-grid";
export const ProductList = () => {
const { dataGridProps } = useDataGrid();
const columns: GridColDef[] = [
{ field: "id", headerName: "ID", width: 70 },
{ field: "name", headerName: "Name", flex: 1 },
{
field: "price",
headerName: "Price",
type: "number",
width: 130,
},
{
field: "actions",
headerName: "Actions",
renderCell: ({ row }) => (
<>
<EditButton hideText recordItemId={row.id} />
<ShowButton hideText recordItemId={row.id} />
</>
),
width: 150,
},
];
return (
<List>
<DataGrid {...dataGridProps} columns={columns} autoHeight />
</List>
);
};
Create
Create form with Material-UI components:pages/products/create.tsx
import { Create, useForm } from "@refinedev/mui";
import { Box, TextField } from "@mui/material";
import { useForm as useHookForm } from "@refinedev/react-hook-form";
export const ProductCreate = () => {
const {
saveButtonProps,
refineCore: { formLoading },
register,
formState: { errors },
} = useForm();
return (
<Create isLoading={formLoading} saveButtonProps={saveButtonProps}>
<Box
component="form"
sx={{ display: "flex", flexDirection: "column", gap: 2 }}
autoComplete="off"
>
<TextField
{...register("name", {
required: "Name is required",
})}
error={!!errors.name}
helperText={errors.name?.message}
label="Name"
name="name"
/>
<TextField
{...register("price", {
required: "Price is required",
})}
error={!!errors.price}
helperText={errors.price?.message}
label="Price"
name="price"
type="number"
/>
<TextField
{...register("description")}
label="Description"
name="description"
multiline
rows={4}
/>
</Box>
</Create>
);
};
Edit
Edit form with automatic data fetching:pages/products/edit.tsx
import { Edit, useForm } from "@refinedev/mui";
import { Box, TextField } from "@mui/material";
export const ProductEdit = () => {
const {
saveButtonProps,
refineCore: { queryResult, formLoading },
register,
formState: { errors },
} = useForm();
return (
<Edit isLoading={formLoading} saveButtonProps={saveButtonProps}>
<Box
component="form"
sx={{ display: "flex", flexDirection: "column", gap: 2 }}
>
<TextField
{...register("id")}
disabled
label="ID"
name="id"
/>
<TextField
{...register("name", {
required: "Name is required",
})}
error={!!errors.name}
helperText={errors.name?.message}
label="Name"
name="name"
/>
<TextField
{...register("price", {
required: "Price is required",
})}
error={!!errors.price}
helperText={errors.price?.message}
label="Price"
name="price"
type="number"
/>
</Box>
</Edit>
);
};
Show
Display single record:pages/products/show.tsx
import {
Show,
TextField,
NumberField,
DateField,
} from "@refinedev/mui";
import { useShow } from "@refinedev/core";
import { Typography, Stack } from "@mui/material";
export const ProductShow = () => {
const { queryResult } = useShow();
const { data, isLoading } = queryResult;
const record = data?.data;
return (
<Show isLoading={isLoading}>
<Stack gap={1}>
<Typography variant="body1" fontWeight="bold">
ID
</Typography>
<TextField value={record?.id} />
<Typography variant="body1" fontWeight="bold">
Name
</Typography>
<TextField value={record?.name} />
<Typography variant="body1" fontWeight="bold">
Price
</Typography>
<NumberField
value={record?.price}
options={{
style: "currency",
currency: "USD",
}}
/>
<Typography variant="body1" fontWeight="bold">
Created At
</Typography>
<DateField value={record?.createdAt} />
</Stack>
</Show>
);
};
Field Components
Material-UI field components for displaying data:import {
TextField,
NumberField,
DateField,
EmailField,
UrlField,
BooleanField,
TagField,
MarkdownField,
} from "@refinedev/mui";
import { Stack } from "@mui/material";
function FieldExamples() {
return (
<Stack spacing={2}>
<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" />
<MarkdownField value="# Hello\n\nThis is **markdown**" />
</Stack>
);
}
Button Components
Material-UI styled action buttons:import {
CreateButton,
EditButton,
ShowButton,
DeleteButton,
ListButton,
RefreshButton,
SaveButton,
CloneButton,
ExportButton,
ImportButton,
} from "@refinedev/mui";
import { Stack } from "@mui/material";
function ButtonExamples() {
return (
<Stack direction="row" spacing={1}>
<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 />
</Stack>
);
}
Advanced Features
Data Grid with Filters
import { List, useDataGrid } from "@refinedev/mui";
import { DataGrid, GridColDef } from "@mui/x-data-grid";
export const ProductList = () => {
const { dataGridProps } = useDataGrid({
initialSorter: [
{
field: "id",
order: "desc",
},
],
initialFilter: [
{
field: "status",
operator: "eq",
value: "active",
},
],
});
const columns: GridColDef[] = [
{ field: "id", headerName: "ID", width: 70 },
{
field: "name",
headerName: "Name",
flex: 1,
filterable: true,
},
{
field: "status",
headerName: "Status",
width: 130,
type: "singleSelect",
valueOptions: ["active", "inactive"],
},
{
field: "price",
headerName: "Price",
type: "number",
width: 130,
},
];
return (
<List>
<DataGrid
{...dataGridProps}
columns={columns}
autoHeight
filterMode="server"
sortingMode="server"
/>
</List>
);
};
Modal Forms
import { useModalForm, CreateButton, Create } from "@refinedev/mui";
import { Dialog, DialogContent, TextField, Box } from "@mui/material";
export const ProductList = () => {
const {
modal: { visible, close },
show,
register,
formState: { errors },
saveButtonProps,
} = useModalForm({
refineCoreProps: { action: "create" },
});
return (
<>
<CreateButton onClick={() => show()} />
<Dialog open={visible} onClose={close}>
<DialogContent>
<Create saveButtonProps={saveButtonProps}>
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
<TextField
{...register("name", { required: "Name is required" })}
error={!!errors.name}
helperText={errors.name?.message}
label="Name"
/>
</Box>
</Create>
</DialogContent>
</Dialog>
</>
);
};
Auto-Save Indicator
import { AutoSaveIndicator, useForm } from "@refinedev/mui";
import { Box, TextField } from "@mui/material";
export const ProductEdit = () => {
const {
register,
refineCore: { autoSaveProps },
} = useForm({
refineCoreProps: {
autoSave: {
enabled: true,
debounce: 2000,
},
},
});
return (
<>
<AutoSaveIndicator {...autoSaveProps} />
<Box component="form" sx={{ display: "flex", flexDirection: "column" }}>
<TextField {...register("name")} label="Name" />
</Box>
</>
);
};
Responsive Layout
import { ThemedLayout } from "@refinedev/mui";
import { useMediaQuery, useTheme } from "@mui/material";
function App() {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down("sm"));
return (
<ThemedLayout
Sider={() => (
<ThemedSider
render={({ items, logout, collapsed }) => (
// Custom sider rendering
<>{items}</>
)}
/>
)}
>
{/* Your routes */}
</ThemedLayout>
);
}
Next Steps
Material-UI Docs
Official Material-UI documentation
Examples
See complete MUI examples
DataGrid
Learn about MUI DataGrid
Theming
Customize your theme