Skip to main content
🧙‍♂️ refine grants your wishes! Please give us a ⭐️ on GitHub to keep the magic going.
Version: 4.xx.xx
Swizzle Ready
Source Code

<ThemedLayout>

<ThemedLayoutV2> component uses the <Layout> and <Sider> components from the Antd Design library to define the layout and structure of a web page. It includes customizable components for the header, sidebar, title, footer, and off-layout area, which can be replaced or customized as needed.

By using <ThemedLayoutV2>, developers can create a consistent look and feel across multiple pages or sections of a website, while also improving code maintainability and reusability. The customizable sections of <ThemedLayoutV2> include:

  • <ThemedHeaderV2>: displayed at the top of the page and can display the user's name and avatar.
  • <ThemedSiderV2>: displayed on the left side of the page and can display menu items.
  • <ThemedTitleV2>: displayed at the top of <ThemedSiderV2> and includes an icon and text.
  • <Footer>: displayed at the bottom of the page.
  • <OffLayoutArea>: rendered outside of the main layout component and can be placed anywhere on the page while still being part of the overall layout.

Footer and OffLayoutArea do not have any default components.

Usage

We'll show what default <ThemedLayoutV2> looks like in the following example.

localhost:3000/samples
import { Refine } from "@refinedev/core";
import { ThemedLayoutV2, RefineThemes } from "@refinedev/antd";
import { ConfigProvider } from "antd";
import { AntdInferencer } from "@refinedev/inferencer/antd";

import routerProvider from "@refinedev/react-router-v6";
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";

import dataProvider from "@refinedev/simple-rest";

import { authProvider } from "./authProvider";

const API_URL = "https://api.fake-rest.refine.dev";

const App: React.FC = () => {
return (
<BrowserRouter>
<ConfigProvider theme={RefineThemes.Blue}>
<Refine
routerProvider={routerProvider}
dataProvider={dataProvider(API_URL)}
authProvider={authProvider}
resources={[
{
name: "samples",
list: "/samples",
},
]}
>
<Routes>
<Route
element={
<ThemedLayoutV2>
<Outlet />
</ThemedLayoutV2>
}
>
<Route
path="/samples"
element={<AntdInferencer />}
/>
</Route>
</Routes>
</Refine>
</ConfigProvider>
</BrowserRouter>
);
};
NOTE

<ThemedLayoutV2> is designed to be responsive. In the live-preview, it appears in tablet mode and uses <Drawer>. On larger screens, it will use <Sider>.

INFORMATION

Example of above showing how to use <ThemedLayoutV2> with React Router v6. You can see these examples for other routers:

Props

Sider

In <ThemedLayoutV2>, the sidebar section is rendered using the <ThemedSiderV2> component by default. This component is specifically designed to generate menu items based on the resources defined in <Refine> components, using the useMenu hook. However, if desired, it's possible to replace the default <ThemedSiderV2> component by passing a custom component to the Sider prop.

import { Refine } from "@refinedev/core";
import { ThemedLayoutV2 } from "@refinedev/antd";

import { CustomSider } from "./CustomSider";

const App: React.FC = () => {
return (
<Refine
// ...
>
<ThemedLayoutV2
Sider={() => <CustomSider />}
>
{/* ... */}
</ThemedLayoutV2>
</Refine>
);
};

Also, you can customize the default <ThemedSiderV2> component either by using its props or with the swizzle feature.

Here is an example of how to customize the default <ThemedSiderV2> component using the render and Title prop:

import { Refine } from "@refinedev/core";
import { ThemedLayoutV2, ThemedSiderV2 } from "@refinedev/antd";

import { CustomTitle } from "./CustomTitle";

const App: React.FC = () => {
return (
<Refine
// ...
>
<ThemedLayoutV2
Sider={() => (
<ThemedSiderV2
Title={({ collapsed }) => (
<CustomTitle collapsed={collapsed} />
)}
render={({ items, logout, collapsed }) => {
return (
<>
<div>My Custom Element</div>
{items}
{logout}
</>
);
}}
/>
)}
>
{/* ... */}
</ThemedLayoutV2>
</Refine>
);
};

Also, you can make the sidebar fixed by passing the fixed property, which is optional and false by default. You can see the usage as follows:

import { Refine } from "@refinedev/core";
import { ThemedLayoutV2, ThemedSiderV2 } from "@refinedev/antd";

const App: React.FC = () => {
return (
<Refine
// ...
>
<ThemedLayoutV2
Sider={() => <ThemedSiderV2 fixed />}
>
{/* ... */}
</ThemedLayoutV2>
</Refine>
);
};

Sider Props

PropTypeDescription
TitleReact.FCComponent to render at the top
renderSiderRenderFunctionFunction to render the menu items and other elements inside the <ThemedSiderV2>
metaRecord<string,any>Meta data to use when creating routes for the menu items
fixedbooleanWhether the sider is fixed or not
activeItemDisabledbooleanWhether clicking on an active sider item should reload the page
type SiderRenderFunction = (props: {
items: JSX.Element[];
logout: React.ReactNode;
dashboard: React.ReactNode;
collapsed: boolean;
}) => React.ReactNode;

initialSiderCollapsed

This prop is used to set the initial collapsed state of the <ThemedSiderV2> component.

<ThemedLayoutV2
initialSiderCollapsed={true}
>
{/* ... */}
</ThemedLayoutV2>

In <ThemedLayoutV2>, the header section is rendered using the <ThemedHeaderV2> component by default. It uses useGetIdentity hook to display the user's name and avatar on the right side of the header. However, if desired, it's possible to replace the default <ThemedHeaderV2> component by passing a custom component to the Header prop.

Here is an example of how to replace the default <ThemedHeaderV2> component:

import { Refine } from "@refinedev/core";
import { ThemedLayoutV2 } from "@refinedev/antd";

import { CustomHeader } from "./CustomHeader";

const App: React.FC = () => {
return (
<Refine
// ...
>
<ThemedLayoutV2
Header={() => <CustomHeader />}
>
{/* ... */}
</ThemedLayoutV2>
</Refine>
);
};

You can also make it sticky using the sticky property:

import { Refine } from "@refinedev/core";
import {
ThemedLayoutV2,
ThemedHeaderV2,
} from "@refinedev/antd";

const App: React.FC = () => {
return (
<Refine
// ...
>
<ThemedLayoutV2
Header={() => <ThemedHeaderV2 sticky />}
>
{/* ... */}
</ThemedLayoutV2>
</Refine>
);
};

Title

In <ThemedLayoutV2>, the title section is rendered using the <ThemedTitleV2> component by default. However, if desired, it's possible to replace the default <ThemedTitleV2> component by passing a custom component to the Title prop.

Here is an example of how to replace the default <ThemedTitleV2> component:

import { Refine } from "@refinedev/core";
import { ThemedLayoutV2, ThemedTitleV2 } from "@refinedev/antd";

import { MyLargeIcon, MySmallIcon } from "./MyIcon";

const App: React.FC = () => {
return (
<Refine
// ...
>
<ThemedLayoutV2
Title={({ collapsed }) => (
<ThemedTitleV2
// collapsed is a boolean value that indicates whether the <Sidebar> is collapsed or not
collapsed={collapsed}
icon={collapsed ? <MySmallIcon /> : <MyLargeIcon />}
text="My Project"
/>
)}
>
{/* ... */}
</ThemedLayoutV2>
</Refine>
);
};

The footer section of the layout is displayed at the bottom of the page. refine doesn't provide a default footer component. However, you can pass a custom component to the Footer prop to display a footer section.

Here is an example of how to display a footer section:

localhost:3000/samples
import { Refine } from "@refinedev/core";
import { ThemedLayoutV2, RefineThemes } from "@refinedev/antd";
import { ConfigProvider, Layout } from "antd";
import { AntdInferencer } from "@refinedev/inferencer/antd";

import routerProvider from "@refinedev/react-router-v6";
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";

import dataProvider from "@refinedev/simple-rest";

import { authProvider } from "./authProvider";

const API_URL = "https://api.fake-rest.refine.dev";

const App: React.FC = () => {
return (
<BrowserRouter>
<ConfigProvider theme={RefineThemes.Blue}>
<Refine
routerProvider={routerProvider}
dataProvider={dataProvider(API_URL)}
authProvider={authProvider}
resources={[
{
name: "samples",
list: "/samples",
},
]}
>
<Routes>
<Route
element={
<ThemedLayoutV2
Footer={() => (
<Layout.Footer
style={{
textAlign: "center",
color: "#fff",
backgroundColor: "#7dbcea",
}}
>
My Custom Footer
</Layout.Footer>
)}
>
<Outlet />
</ThemedLayoutV2>
}
>
<Route path="samples">
<Route index element={<AntdInferencer />} />
</Route>
</Route>
</Routes>
</Refine>
</ConfigProvider>
</BrowserRouter>
);
};
import { Refine } from "@refinedev/core";
import { ThemedLayoutV2 } from "@refinedev/antd";
import { Layout } from "antd";

const App: React.FC = () => {
return (
<Refine
// ...
>
<ThemedLayoutV2
Footer={() => (
<Layout.Footer
style={{
textAlign: "center",
color: "#fff",
backgroundColor: "#7dbcea",
}}
>
My Custom Footer
</Layout.Footer>
)}
>
{/* ... */}
</ThemedLayoutV2>
</Refine>
);
};

OffLayoutArea

Used to component is rendered outside of the main layout component, allowing it to be placed anywhere on the page while still being part of the overall layout .refine doesn't provide a default off-layout area component. However, you can pass a custom component to the OffLayoutArea prop to display a custom off-layout area.

Here is an example of how to display a custom off-layout area:

localhost:3000/samples
import { Refine } from "@refinedev/core";
import { ThemedLayoutV2, RefineThemes } from "@refinedev/antd";
import { ConfigProvider, Button } from "antd";
import { AntdInferencer } from "@refinedev/inferencer/antd";

import routerProvider from "@refinedev/react-router-v6";
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";

import dataProvider from "@refinedev/simple-rest";

import { authProvider } from "./authProvider";

const API_URL = "https://api.fake-rest.refine.dev";

const App: React.FC = () => {
return (
<BrowserRouter>
<ConfigProvider theme={RefineThemes.Blue}>
<Refine
routerProvider={routerProvider}
dataProvider={dataProvider(API_URL)}
authProvider={authProvider}
resources={[
{
name: "samples",
list: "/samples",
},
]}
>
<Routes>
<Route
element={
<ThemedLayoutV2
OffLayoutArea={() => (
<Button
type="primary"
size="small"
onClick={() =>
alert("Off layout are clicked")
}
style={{
position: "fixed",
left: "8px",
bottom: "8px",
zIndex: 1000,
}}
>
Send us Feedback 👋
</Button>
)}
>
<Outlet />
</ThemedLayoutV2>
}
>
<Route
path="/samples"
element={<AntdInferencer />}
/>
</Route>
</Routes>
</Refine>
</ConfigProvider>
</BrowserRouter>
);
};
import { Refine } from "@refinedev/core";
import { ThemedLayoutV2 } from "@refinedev/antd";
import { Button } from "antd";

const App: React.FC = () => {
return (
<Refine
// ...
>
<ThemedLayoutV2
OffLayoutArea={() => (
<Button
type="primary"
size="small"
onClick={() => alert("Off layout are clicked")}
style={{
position: "fixed",
left: "8px",
bottom: "8px",
zIndex: 1000,
}}
>
Send us Feedback 👋
</Button>
)}
>
{/* ... */}
</ThemedLayoutV2>
</Refine>
);
};

Customizing with swizzle

🚨 This feature can be used with @refine/cli. Please refer to CLI documentation for more information.

<ThemedLayoutV2> component source code can be ejecting using the swizzle command. This will create a copy of the component in your project's src directory, allowing you to customize as your needs.

Usage

Let's create a new component by swizzling the <ThemedLayoutV2> components.

> npm run refine swizzle

? Which package do you want to swizzle? (Use arrow keys or type to search)

Data Provider
◯ @refinedev/simple-rest
UI Framework
◉ @refinedev/antd

First, you need to select the package you want to swizzle. In this example, we will swizzle the @refinedev/antd package.

INFORMATION

refine CLI will only show the packages that are installed in your project.

? Which component do you want to swizzle?

◯ TagField
◯ TextField
◯ UrlField
Other
◯ Breadcrumb
❯◉ ThemedLayoutV2
Pages
◯ ErrorPage
◯ AuthPage
(Move up and down to reveal more choices)

Then, you need to select the component you want to swizzle. In this example, we will swizzle the ThemedLayoutV2 component.

Successfully swizzled Themed Layout
Files created:
- src/components/themedLayout/sider.tsx
- src/components/themedLayout/header.tsx
- src/components/themedLayout/title.tsx
- src/components/themedLayout/index.tsx

Warning:
If you want to change the default layout;
You should pass layout related components to the <ThemedLayoutV2/> component's props.

╭ App.tsx ───────────────────────────────────────────────────────────────────────────────────────╮
│ │
import { ThemedLayoutV2 } from "components/themedLayout";
import { ThemedHeaderV2 } from "components/themedLayout/header";
import { ThemedSiderV2 } from "components/themedLayout/sider";
import { ThemedTitleV2 } from "components/themedLayout/title";
│ │
│ const App = () => {
return (
<Refine │
│ /* ... */ │
>
<ThemedLayoutV2 │
Header={ThemedHeaderV2}
Sider={ThemedSiderV2}
Title={ThemedTitleV2}
│ />
│ /* ... */ │
</ThemedLayoutV2>
</Refine>
);
}
│ │
╰────────────────────────────────────────────────────────────────────────────────────────────────╯

Finally, the swizzle command will create a new folder in the src/components/layout directory and generate the layout components of the @refinedev/antd package in it.

You can use these components in your project as you wish.

import { Refine } from "@refinedev/core";
import { ThemedLayoutV2 } from "components/themedLayout";
import { ThemedHeaderV2 } from "components/themedLayout/header";
import { ThemedSiderV2 } from "components/themedLayout/sider";
import { ThemedTitleV2 } from "components/themedLayout/title";

const App = () => {
return (
<Refine
/* ... */
>
<ThemedLayoutV2
Header={ThemedHeaderV2}
Sider={ThemedSiderV2}
Title={ThemedTitleV2}
>
/* ... */
</ThemedLayoutV2>
</Refine>
);
};
INFORMATION

refine CLI determines the path to create a new folder according to the framework you are using. For example, if you are using the remix, the path will be app/components/layout.

CAUTION

If there is already a file with the same name in the directory, the swizzle command will not overwrite it.

Migrate ThemedLayout to ThemedLayoutV2

Fixed some UI problems with ThemedLayoutV2. If you are still using ThemedLayout you can update it by following these step.

src/App.tsx
-import { ThemedLayout } from "@refinedev/antd";
+import { ThemedLayoutV2 } from "@refinedev/antd";
...
-<ThemedLayout>
+<ThemedLayoutV2>
<Outlet />
-</ThemedLayout>
+</ThemedLayoutV2>
...

collapse/uncollapse Sider component with useThemedLayoutContext hook

The useThemedLayoutContext hook is that is used to collapse/uncollapse the Sider component. You can do this anywhere you want using the useThemedLayoutContext hook. Below you can see an example put on the dashboard page.

localhost:3000/
import { Refine } from "@refinedev/core";
import { ThemedLayoutV2, RefineThemes, useThemedLayoutContext } from "@refinedev/antd";
import { ConfigProvider, Button, Space } from "antd";
import { AntdInferencer } from "@refinedev/inferencer/antd";

import routerProvider from "@refinedev/react-router-v6";
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";

import dataProvider from "@refinedev/simple-rest";

import { authProvider } from "./authProvider";

const API_URL = "https://api.fake-rest.refine.dev";

const DashboardPage = () => {
const {
siderCollapsed,
setSiderCollapsed,
mobileSiderOpen,
setMobileSiderOpen,
} = useThemedLayoutContext();

return (
<Space style={{ paddingTop: 30 }}>
<Button
type="primary"
onClick={() => setMobileSiderOpen(!mobileSiderOpen)}
>
toggle mobile sider
</Button>
<Button
type="primary"
onClick={() => setSiderCollapsed(!siderCollapsed)}
>
toggle collapse of sider
</Button>
</Space>
);
};

const App: React.FC = () => {
return (
<BrowserRouter>
<ConfigProvider theme={RefineThemes.Blue}>
<Refine
routerProvider={routerProvider}
dataProvider={dataProvider(API_URL)}
authProvider={authProvider}
resources={[
{
name: "dashboard",
list: "/",
},
{
name: "samples",
list: "/samples",
},
]}
>
<Routes>
<Route
element={
<ThemedLayoutV2>
<Outlet />
</ThemedLayoutV2>
}
>
<Route path="/" element={<DashboardPage />} />
<Route
path="/samples"
element={<AntdInferencer />}
/>
</Route>
</Routes>
</Refine>
</ConfigProvider>
</BrowserRouter>
);
};

FAQ

How can I persist the collapsed state of the <ThemedSiderV2> component?

You can use initialSiderCollapsed prop to persist the collapsed state of the <ThemedSiderV2> component.

For example, you can get initialSiderCollapsed's value from localStorage or cookie for persistence between sessions:

src/App.tsx
import { useState } from "react";
import { Refine } from "@refinedev/core";
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
import { ThemedLayoutV2 } from "@refinedev/antd";

const App: React.FC = () => {
// you can get this value from `localStorage` or `cookie`
// for persistence between sessions
const [initialSiderCollapsed, setInitialSiderCollapsed] = useState(true);

return (
<BrowserRouter>
<Refine
// ...
>
{/* ... */}
<Routes>
<Route
element={
<ThemedLayoutV2
initialSiderCollapsed={initialSiderCollapsed}
>
<Outlet />
</ThemedLayoutV2>
}
>
{/* ... */}
</Route>
</Routes>
</Refine>
</BrowserRouter>
);
};

export default App;
```