Layout
If this is a new project, check out the example projects page to be able to clone a template project with everything configured.
When creating an app, a global layout is defined to help the user access common
actions or navigate to different pages. Most web applications will use a fixed
header, a navigation panel that can be toggled or static, and a main content
area. react-md
provides components, and hooks to help create these global
layouts.
Choosing a Layout Type
To get started, determine the type of layout that makes the most sense for the app. Should there be a fixed header at the top of the page or should it scroll with the rest of the content on the page? Should the navigation always be visible, toggleable, expandable, or resizable? Once a general idea has been formed, choose one of the layouts listed below to get started or build your own.
All layout types will not be shown on this page since there are endless possibilities. Check out the AppBar, Sheet, Dialog, and Navigation components to extend the layout.
Temporary Navigation Layout
A temporary navigation layout is common and always used on smaller viewports
for the other layout types. The layout is created by using the
useTemporaryLayout
hook and an AppBar
, Button
, Main
, Sheet
, and some
sort of navigation component. The useTemporaryLayout
provides props for each
component to handle the updating the Sheet
's visibility as the pathname
changes.
Check out the useTemporaryLayout hook documentation for more configuration options.
The code for an example temporary layout is shown below and can be viewed here.
"use client";
import { AppBar } from "@react-md/core/app-bar/AppBar";
import { AppBarTitle } from "@react-md/core/app-bar/AppBarTitle";
import { Button } from "@react-md/core/button/Button";
import { Main } from "@react-md/core/layout/Main";
import { useTemporaryLayout } from "@react-md/core/layout/useTemporaryLayout";
import { Sheet } from "@react-md/core/sheet/Sheet";
import { usePathname } from "next/navigation.js";
import { type ReactElement, type ReactNode } from "react";
import { MainNavigation } from "./MainNavigation.jsx";
export interface TemporaryLayoutExampleProps {
children: ReactNode;
}
export function TemporaryLayoutExample(
props: TemporaryLayoutExampleProps
): ReactElement {
const { children } = props;
const pathname = usePathname();
const { appBarProps, mainProps, temporaryNavProps, navToggleProps } =
useTemporaryLayout({ pathname });
return (
<>
<AppBar {...appBarProps}>
<Button {...navToggleProps} />
<AppBarTitle>Main Title</AppBarTitle>
</AppBar>
<Main {...mainProps}>{children}</Main>
<Sheet {...temporaryNavProps}>
<MainNavigation />
</Sheet>
</>
);
}
Expandable Navigation Layout
An expandable layout extends the temporary layout by allowing larger viewports
like tablets and desktops to be able to view the navigation inline with the
main content. This layout can be created using the useExpandableLayout
hook,
the same components as the temporary layout, and a new LayoutNav
component.
Check out the useExpandableLayout hook documentation for more configuration options.
The code for an example expandable layout is shown below and can be viewed here.
"use client";
import { AppBar } from "@react-md/core/app-bar/AppBar";
import { AppBarTitle } from "@react-md/core/app-bar/AppBarTitle";
import { Button } from "@react-md/core/button/Button";
import { LayoutNav } from "@react-md/core/layout/LayoutNav";
import { Main } from "@react-md/core/layout/Main";
import { useExpandableLayout } from "@react-md/core/layout/useExpandableLayout";
import { Sheet } from "@react-md/core/sheet/Sheet";
import { usePathname } from "next/navigation.js";
import { type ReactElement, type ReactNode } from "react";
import { MainNavigation } from "./MainNavigation.jsx";
export interface ExpandableLayoutExampleProps {
children: ReactNode;
}
export function ExpandableLayoutExample(
props: ExpandableLayoutExampleProps
): ReactElement {
const { children } = props;
const pathname = usePathname();
const {
temporary,
persistent,
temporaryNavProps,
navToggleProps,
appBarProps,
mainProps,
expandableNavProps,
} = useExpandableLayout({ pathname });
return (
<>
<AppBar {...appBarProps}>
<Button {...navToggleProps} />
<AppBarTitle>Expandable Layout Example</AppBarTitle>
</AppBar>
{persistent && (
<LayoutNav {...expandableNavProps}>
<MainNavigation />
</LayoutNav>
)}
{temporary && (
<Sheet {...temporaryNavProps}>
<MainNavigation />
</Sheet>
)}
<Main {...mainProps}>{children}</Main>
</>
);
}
Full Height Navigation Layout
This layout reuses everything from the expandable layout without making the layout expandable and the navigation panel spans the entire height of the screen instead of appearing beneath the fixed app bar.
The diff for creating a full height layout is shown below and can be viewed here.
const {
temporary,
persistent,
temporaryNavProps,
navToggleProps,
appBarProps,
mainProps,
expandableNavProps,
- } = useExpandableLayout({ pathname });
+ } = useExpandableLayout({ pathname, fullHeightNav: "static" });
Full Height Expandable Navigation Layout
This layout is the same as the expandable layout except that the navigation panel is the full height instead of appearing beneath the fixed app bar.
The diff for creating a full height expandable layout is shown below and can be viewed here.
const {
temporary,
persistent,
temporaryNavProps,
navToggleProps,
appBarProps,
mainProps,
expandableNavProps,
- } = useExpandableLayout({ pathname });
+ } = useExpandableLayout({ pathname, fullHeightNav: true });
Resizable Navigation Layout
A resizable layout extends the expandable layout by allowing the user to
manually resize the navigation panel to their desired width by dragging to their
desired width. This layout can be created using the useResizableLayout
hook,
the same components as the expandable layout, and a new LayoutWindowSplitter
component.
Check out the useResizableLayout hook documentation for more configuration options.
The code for an example resizable layout is shown below and can be viewed here.
"use client";
import { AppBar } from "@react-md/core/app-bar/AppBar";
import { AppBarTitle } from "@react-md/core/app-bar/AppBarTitle";
import { Button } from "@react-md/core/button/Button";
import { LayoutNav } from "@react-md/core/layout/LayoutNav";
import { LayoutWindowSplitter } from "@react-md/core/layout/LayoutWindowSplitter";
import { Main } from "@react-md/core/layout/Main";
import { useResizableLayout } from "@react-md/core/layout/useResizableLayout";
import { Sheet } from "@react-md/core/sheet/Sheet";
import { usePathname } from "next/navigation.js";
import { type ReactElement, type ReactNode } from "react";
import { MainNavigation } from "./MainNavigation.jsx";
export interface ResizableLayoutExampleProps {
children: ReactNode;
}
export function ResizableLayoutExample(
props: ResizableLayoutExampleProps
): ReactElement {
const { children } = props;
const pathname = usePathname();
const {
temporary,
persistent,
appBarProps,
navToggleProps,
expandableNavProps,
mainProps,
temporaryNavProps,
windowSplitterProps,
} = useResizableLayout({ pathname });
return (
<>
<AppBar {...appBarProps}>
<Button {...navToggleProps} />
<AppBarTitle>Resizable Layout Example</AppBarTitle>
</AppBar>
{persistent && (
<>
<LayoutNav {...expandableNavProps}>
<MainNavigation />
</LayoutNav>
<LayoutWindowSplitter {...windowSplitterProps} />
</>
)}
{temporary && (
<Sheet {...temporaryNavProps}>
<MainNavigation />
</Sheet>
)}
<Main {...mainProps}>{children}</Main>
</>
);
}
Full Height Resizable Navigation Layout
Just like the expandable layout version, the resizable layout can become full
height by providing fullHeightNav: true
or fullHeightNav: "static"
. The
diff for creating a full height static resizable layout is shown below and can
be viewed here.
const {
temporary,
persistent,
appBarProps,
navToggleProps,
expandableNavProps,
mainProps,
temporaryNavProps,
windowSplitterProps,
- } = useResizableLayout({ pathname });
+ } = useResizableLayout({ pathname, fullHeightNav: "static" });
Creating the MainNavigation
Now that a layout has been setup, it's time to create the MainNavigation
component which is usually one of:
- list of links
- collapsible list of links
- dropdown menus
- tree
Navigation Component
The easiest way to get started is by using the Navigation
component which is
the default navigation type shown in the demos on this page. The Navigation
component accepts a list of items
and data
containing the pathname
and
linkComponent
. The Home, Page 1
, and Page 2
routes in the
demos are created by:
import { Navigation } from "@react-md/core/navigation/Navigation";
import { type NavigationItem } from "@react-md/core/navigation/types";
import { useNavigationExpansion } from "@react-md/core/navigation/useNavigationExpansion";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import HomeIcon from "@react-md/material-icons/HomeIcon";
import StarIcon from "@react-md/material-icons/StarIcon";
import Link from "next/link.js";
import { usePathname } from "next/navigation.js";
import { type ReactElement } from "react";
const items: readonly NavigationItem[] = [
{
type: "route",
href: "/",
children: "Home",
beforeAddon: <HomeIcon />,
},
{
type: "route",
href: "/page-1",
children: "Page 1",
beforeAddon: <StarIcon />,
},
{
type: "route",
href: "/page-2",
children: "Page 2",
beforeAddon: <FavoriteIcon />,
},
];
export function MainNavigation(): ReactElement {
const pathname = usePathname();
const { data } = useNavigationExpansion({
pathname,
linkComponent: Link,
});
return <Navigation items={items} data={data} />;
}
See the Navigation component documentation for additional features, styling, and customization.
Tree Component
Before considering the use of a Tree for site navigation, it is important to understand:
- The
Tree
implementation requires complex functionality that is not needed for typical site navigation - The
Navigation
component can be used instead for expandable groups of links
A Tree can be used for site navigation with the
useLayoutTree hook using the TreeData
data
structure. Each item should have an itemId
that matches the href
for the
route and a parentId
of a parent route or null
. When the itemId
matches
the current pathname
, it will gain the active styles.
A tree layout can be viewed here.
import { useLayoutTree } from "@react-md/core/layout/useLayoutTree";
import { Tree } from "@react-md/core/tree/Tree";
import { type TreeData } from "@react-md/core/tree/types";
import FavoriteIcon from "@react-md/material-icons/FavoriteIcon";
import HomeIcon from "@react-md/material-icons/HomeIcon";
import StarIcon from "@react-md/material-icons/StarIcon";
import Link from "next/link.js";
import { usePathname } from "next/navigation.js";
import { type ReactElement } from "react";
const navItems: TreeData = {
"/": {
parentId: null,
itemId: "/",
href: "/",
children: "Home",
leftAddon: <HomeIcon />,
},
"/page-1": {
itemId: "/page-1",
parentId: null,
href: "/page-1",
children: "Page 1",
leftAddon: <StarIcon />,
},
"/page-1/child-page-1": {
itemId: "/page-1/child-page-1",
parentId: "/page-1",
href: "/page-1/child-page-1",
children: "Child Page 1",
},
"/page-2": {
itemId: "/page-2",
parentId: null,
href: "/page-2",
children: "Page 2",
leftAddon: <FavoriteIcon />,
},
};
export function MainNavigation(): ReactElement {
const pathname = usePathname();
const tree = useLayoutTree({
navItems,
pathname,
});
return <Tree aria-label="Navigation" {...tree} linkComponent={Link} />;
}
See the Tree component documentation for additional features, styling, and customization.