Using Layouts
Having wrapped our app with the necessary styling components, we're now ready to add our layout into the application. Refine provides default layouts for its supported UI libraries through <ThemedLayoutV2 />
components, delivering a two-column layout with a sidebar and a main content area.
<ThemedLayoutV2 />
components includes an header with user information (if an authProvider
is provided), a sidebar with navigation links based on your resource definitions, a logout button (if an authProvider
is provided), and a main content area where your content will be rendered.
Notice that we've removed the <Header />
component from our app since the layout already includes the same features.
Adding a Custom Title
Layout components are a composition of smaller components and they can be customized by providing respective props. The <ThemedLayoutV2 />
component consists of the following components:
<ThemedHeaderV2 />
for the header and can be customized viaHeader
prop.<ThemedSiderV2 />
for the sidebar and can be customized viaSider
prop.<ThemedTitleV2 />
for the logo and the title of the app and can be customized viaTitle
prop.- There are also
Footer
andOffLayoutArea
props for the footer and off-layout area respectively but they don't have default components for the layout.
Let's change the title of our app with using the Title
prop of the <ThemedLayoutV2 />
component and the <ThemedTitleV2 />
component.
Update your src/App.tsx
file by adding the following lines:
import { Refine, Authenticated } from "@refinedev/core";
import routerProvider, { NavigateToResource } from "@refinedev/react-router-v6";
import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";
import { RefineThemes, ThemedLayoutV2, ThemedTitleV2 } from "@refinedev/mui";
import CssBaseline from "@mui/material/CssBaseline";
import GlobalStyles from "@mui/material/GlobalStyles";
import { ThemeProvider } from "@mui/material/styles";
import { dataProvider } from "./providers/data-provider";
import { authProvider } from "./providers/auth-provider";
import { ShowProduct } from "./pages/products/show";
import { EditProduct } from "./pages/products/edit";
import { ListProducts } from "./pages/products/list";
import { CreateProduct } from "./pages/products/create";
import { Login } from "./pages/login";
export default function App(): JSX.Element {
return (
<BrowserRouter>
<ThemeProvider theme={RefineThemes.Blue}>
<CssBaseline />
<GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} />
<Refine
dataProvider={dataProvider}
authProvider={authProvider}
routerProvider={routerProvider}
resources={[
{
name: "protected-products",
list: "/products",
show: "/products/:id",
edit: "/products/:id/edit",
create: "/products/create",
meta: { label: "Products" },
},
]}
>
<Routes>
<Route
element={
<Authenticated
key="authenticated-routes"
redirectOnFail="/login"
>
<ThemedLayoutV2
Title={(props) => (
<ThemedTitleV2 {...props} text="Awesome Project" />
)}
>
<Outlet />
</ThemedLayoutV2>
</Authenticated>
}
>
{/* ... */}
</Route>
{/* ... */}
</Routes>
</Refine>
</ThemeProvider>
</BrowserRouter>
);
}
Our app is now wrapped with a customized layout that includes a sidebar and a main content area. We've updated our app's name with a single line of code, rest of the props works in the same manner.
Notice that our protected-products
resource is listed in the sidebar with "Products"
label. This is because we've provided a custom label for our resource in the meta.label
field of our resource definition.
In the next step, we'll be refactoring our action components to use forms and tables from Material UI.
import { Refine, Authenticated } from "@refinedev/core"; import routerProvider, { NavigateToResource } from "@refinedev/react-router-v6"; import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom"; import { RefineThemes, ThemedLayoutV2 } from "@refinedev/mui"; import CssBaseline from "@mui/material/CssBaseline"; import GlobalStyles from "@mui/material/GlobalStyles"; import { ThemeProvider } from "@mui/material/styles"; import { dataProvider } from "./providers/data-provider"; import { authProvider } from "./providers/auth-provider"; import { ShowProduct } from "./pages/products/show"; import { EditProduct } from "./pages/products/edit"; import { ListProducts } from "./pages/products/list"; import { CreateProduct } from "./pages/products/create"; import { Login } from "./pages/login"; export default function App(): JSX.Element { return ( <BrowserRouter> <ThemeProvider theme={RefineThemes.Blue}> <CssBaseline /> <GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} /> <Refine dataProvider={dataProvider} authProvider={authProvider} routerProvider={routerProvider} resources={[ { name: "protected-products", list: "/products", show: "/products/:id", edit: "/products/:id/edit", create: "/products/create", meta: { label: "Products" }, }, ]} > <Routes> <Route element={ <Authenticated key="authenticated-routes" redirectOnFail="/login" > <ThemedLayoutV2> <Outlet /> </ThemedLayoutV2> </Authenticated> } > <Route index element={<NavigateToResource resource="protected-products" />} /> <Route path="/products"> <Route index element={<ListProducts />} /> <Route path=":id" element={<ShowProduct />} /> <Route path=":id/edit" element={<EditProduct />} /> <Route path="create" element={<CreateProduct />} /> </Route> </Route> <Route element={ <Authenticated key="auth-pages" fallback={<Outlet />}> <NavigateToResource resource="protected-products" /> </Authenticated> } > <Route path="/login" element={<Login />} /> </Route> </Routes> </Refine> </ThemeProvider> </BrowserRouter> ); }