Theme
Theme specifies the color of the components, the darkness of the surfaces, level of shadow, appropriate opacity of ink elements, etc. You can either create your own Theme or use Themes that provide from Refine. There are two types of Themes: LightTheme
and DarkTheme
. LightTheme
tend to have dark text on a light background, while DarkTheme
have light text on a dark background. Theme provides a way to your app's design to meet them.
Refer to the Material UI documentation for more information about Material UI Theming. →
Theme Provider
The ThemeProvider
component is a simple wrapper around React's Context API that allows you to inject a Theme object into your application. By default, Material-UI components come with a default Theme. In addition, you can also use the ThemeProvider component to inject a custom theme that you have created yourself. This is a feature that allows for great flexibility in how you design your application.
import { Refine } from '@pankod/refine-core'
import {
Layout,
ErrorComponent,
ReadyPage,
ThemeProvider,
CssBaseline,
GlobalStyles,
} from '@pankod/refine-mui'
import dataProvider from '@pankod/refine-simple-rest'
import routerProvider from '@pankod/refine-react-router-v6'
import { PostsList, PostCreate, PostEdit } from 'pages/posts'
const App: React.FC = () => {
return (
<ThemeProvider theme={YOUR_THEME_OBJECT}>
<CssBaseline />
<GlobalStyles styles={{ html: { WebkitFontSmoothing: 'auto' } }} />
<Refine
routerProvider={routerProvider}
dataProvider={dataProvider('https://api.fake-rest.refine.dev')}
ReadyPage={ReadyPage}
Layout={Layout}
catchAll={<ErrorComponent />}
resources={[
{
name: 'posts',
list: PostsList,
create: PostCreate,
edit: PostEdit,
},
]}
/>
</ThemeProvider>
)
}
export default App
We recommend using create refine-app
to initialize your Refine projects. It configures the project according to your needs including SSR and Theme with Next.js.
Passing the Theme to ThemeProvider
In order to use the theme in your app, you just have one choice: pass it on! Refine provide two types of themes LightTheme
and DarkTheme
.
If you don't wrap your app with ThemeProvider
and theme, it looks like this when using the Material UI default:
In our example, we will be using LightTheme.
The design will change to match the LightTheme
, so you can enjoy these amazing interfaces without any hassle!
Overriding Variables
The best way to customize your theme is by changing the configuration variables. These sections cover some of those most important options, like palette
and typography
!
import { LightTheme } from '@pankod/refine-mui'
const overridedLightTheme = {
...LightTheme,
palette: {
...LightTheme.palette,
primary: {
main: '#44d0c7',
},
secondary: {
main: '#2f82f1',
},
},
}
Get a designer's opinion anyway - you'll be happy with the end result!
When we easy-override our LightTheme, it's going to look like this:
You can also change the Default Font Family. Refine uses the Montserrat
font family by default and you can change it like this:
import {
LightTheme,
TypographyVariantsOptions,
} from '@pankod/refine-mui'
const typography: TypographyVariantsOptions = {
fontFamily: [
'Montserrat',
'-apple-system',
'BlinkMacSystemFont',
'"Segoe UI"',
'Roboto',
'"Helvetica Neue"',
'Arial',
'sans-serif',
'"Apple Color Emoji"',
'"Segoe UI Emoji"',
'"Segoe UI Symbol"',
].join(','),
}
const overridedLightTheme = {
...LightTheme,
...typography,
}
If you are overriding the fontFamily
in typography, you can add the <link>
tags in your index.html
like the following:
<!DOCTYPE html>
<html lang="en">
<head>
<link
href="https://fonts.googleapis.com/css2?family=Montserrat:wght@100;200;300;400;500;600;700;800;900&display=swap"
rel="stylesheet"
/>
<title>refine adding font family example</title>
</head>
<body>
...
</body>
</html>
Create Custom Theme
With the help of Refine's themes, you can customize your site in a matter of minutes. Alternatively, there is also an option to create a custom theme with the createTheme()
method so you can create a custom theme with the configuration variables and use it in the whole application.
You can use the responsiveFontSizes() helper to make Typography font sizes in your theme automated.
For more information, you can review responsiveFontSizes()
in the mui document.
Create Theme with Custom Variables
Creating a theme with default variables is easy and we can see it in the example above. You can also create your theme with custom variables, you can check it out our example
const customTheme = createTheme({
customVariable: {
custom: '#330f49',
},
})
You need to use module augmentation
for the theme to accept your custom values.
import '@pankod/refine-mui'
export interface CustomTheme {
customVariable: {
custom: string
}
}
declare module '@pankod/refine-mui' {
interface Theme extends import('@pankod/refine-mui').Theme, CustomTheme {}
interface ThemeOptions
extends import('@pankod/refine-mui').ThemeOptions,
CustomTheme {}
}
You can see an example of how to create your own theme with custom variables
and its interface
by accessing the links.
Dark Mode
You might prefer to use dark mode in your applications. If you want to add dark mode to your application, you can easily use useMediaQuery
to set your color mode or dynamic toggle to switch your mode by using a context. This will help you maintain a consistent look and feel throughout your app.
System Preference
With the useMediaQuery
hook, you can query a user's preference for light or dark mode and then adjust your site accordingly. This will make things easier on those who prefer darker colors as it simplifies their experience by eliminating any confusion about what browser they are using!
For example:
import { Refine } from '@pankod/refine-core'
import {
Layout,
ErrorComponent,
ReadyPage,
ThemeProvider,
CssBaseline,
GlobalStyles,
useMediaQuery,
} from '@pankod/refine-mui'
import dataProvider from '@pankod/refine-simple-rest'
import routerProvider from '@pankod/refine-react-router-v6'
import { SampleList, SampleCreate, SampleEdit, SampleShow } from 'pages/samples'
const App: React.FC = () => {
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)')
return (
<ThemeProvider theme={prefersDarkMode ? DarkTheme : LightTheme}>
<CssBaseline />
<GlobalStyles styles={{ html: { WebkitFontSmoothing: 'auto' } }} />
<Refine
routerProvider={routerProvider}
dataProvider={dataProvider('https://api.fake-rest.refine.dev')}
ReadyPage={ReadyPage}
Layout={Layout}
catchAll={<ErrorComponent />}
resources={[
{
name: 'samples',
list: SampleList,
create: SampleCreate,
edit: SampleEdit,
show: SampleShow,
},
]}
/>
</ThemeProvider>
)
}
export default App
Dark Mode Toggle
Control the Dark Mode with just one click! We prepared an example that shows how you can manage to toggle Dark Mode with help of a context in your Header component, which is given as a prop to Refine.
Dark Mode Toggle Code Example
ColorModeContext
import React, {
PropsWithChildren,
createContext,
useEffect,
useState,
} from 'react'
import { ThemeProvider } from '@pankod/refine-mui'
import { DarkTheme, LightTheme } from '@pankod/refine-mui'
type ColorModeContextType = {
mode: string
setMode: () => void
}
export const ColorModeContext = createContext<ColorModeContextType>(
{} as ColorModeContextType,
)
export const ColorModeContextProvider: React.FC<PropsWithChildren> = ({
children,
}) => {
const colorModeFromLocalStorage = localStorage.getItem('colorMode')
const isSystemPreferenceDark = window?.matchMedia(
'(prefers-color-scheme: dark)',
).matches
const systemPreference = isSystemPreferenceDark ? 'dark' : 'light'
const [mode, setMode] = useState(
colorModeFromLocalStorage || systemPreference,
)
useEffect(() => {
window.localStorage.setItem('colorMode', mode)
}, [mode])
const setColorMode = () => {
if (mode === 'light') {
setMode('dark')
} else {
setMode('light')
}
}
return (
<ColorModeContext.Provider
value={{
setMode: setColorMode,
mode,
}}
>
<ThemeProvider theme={mode === 'light' ? LightTheme : DarkTheme}>
{children}
</ThemeProvider>
</ColorModeContext.Provider>
)
}
App.tsx
import { Refine } from '@pankod/refine-core'
import {
Layout,
ErrorComponent,
ReadyPage,
CssBaseline,
RefineSnackbarProvider,
notificationProvider,
AppBar,
IconButton,
Box,
Stack,
} from '@pankod/refine-mui'
import dataProvider from '@pankod/refine-simple-rest'
import routerProvider from '@pankod/refine-react-router-v6'
import { LightModeOutlines, DarkModeOutlined } from './icons'
import { SampleList, SampleCreate, SampleEdit, SampleShow } from 'pages/samples'
import { ColorModeContextProvider, ColorModeContext } from './contexts'
const Header = () => {
const { mode, setMode } = useContext(ColorModeContext)
return (
<AppBar color="default" position="sticky">
<Stack width="100%" direction="row" justifyContent="end">
<Box marginRight="20px">
<IconButton
onClick={() => {
setMode()
}}
>
{mode === 'dark' ? <LightModeOutlined /> : <DarkModeOutlined />}
</IconButton>
</Box>
</Stack>
</AppBar>
)
}
const App: React.FC = () => {
return (
<ColorModeContextProvider>
<CssBaseline />
<RefineSnackbarProvider>
<Refine
notificationProvider={notificationProvider}
routerProvider={routerProvider}
dataProvider={dataProvider('https://api.fake-rest.refine.dev')}
ReadyPage={ReadyPage}
Layout={Layout}
catchAll={<ErrorComponent />}
Header={Header}
resources={[
{
name: 'samples',
list: SampleList,
create: SampleCreate,
edit: SampleEdit,
show: SampleShow,
},
]}
/>
</RefineSnackbarProvider>
</ColorModeContextProvider>
)
}
export default App
You can use this CodeSandbox link to access this example. →
Notification Snackbars compatible with Theme
We use the notistack
library for notifications in our Material UI package provides an elegant way to engage with your users.
The main motivation for us to use the Notistack was that while the Notistack provider ( <SnackbarProvider>
) is a child of our ThemeProvider, it works in harmony with the theme.
We provide <RefineSnackbarProvider>
that extended <SnackbarProvider>
with theme style. You have to wrap Refine with <RefineSnackbarProvider>
and also pass the notificationProvider
as props.
If you want to use notistack snackbars with the default style, simply wrap Refine in <SnackbarProvider>
.