Next.js Layout Components
When building web applications, maintaining a consistent look and feel across different pages is crucial for good user experience. Next.js provides a powerful concept called Layout Components that helps you achieve this consistency while keeping your code clean and DRY (Don't Repeat Yourself).
What are Layout Components?
Layout components in Next.js are reusable React components that wrap around your page content, providing consistent UI elements like headers, navigation bars, footers, and sidebars across multiple pages.
Instead of duplicating these elements on every page, you can define them once in a layout component and reuse them throughout your application.
Basic Layout Component Structure
Let's create a simple layout component that includes a header and footer:
// components/Layout.js
import Header from './Header';
import Footer from './Footer';
export default function Layout({ children }) {
return (
<>
<Header />
<main>{children}</main>
<Footer />
</>
);
}
In this example:
- We import
Header
andFooter
components - We define a
Layout
component that acceptschildren
as props - The
children
will be the page content that gets wrapped by our layout
Using Layout Components with Pages
Method 1: Wrapping Individual Pages
You can wrap your page components with the layout:
// pages/index.js
import Layout from '../components/Layout';
export default function HomePage() {
return (
<Layout>
<h1>Welcome to my website!</h1>
<p>This is the home page content.</p>
</Layout>
);
}
// pages/about.js
import Layout from '../components/Layout';
export default function AboutPage() {
return (
<Layout>
<h1>About Us</h1>
<p>Learn more about our company here.</p>
</Layout>
);
}
Method 2: Using Custom App Component (Next.js Pages Router)
For pages router applications, you can apply a layout to all pages by customizing the _app.js
file:
// pages/_app.js
import Layout from '../components/Layout';
export default function MyApp({ Component, pageProps }) {
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
This approach applies the layout to every page in your application automatically.
Method 3: Using App Router's Layout Convention (Next.js 13+)
In Next.js 13+ with the App Router, you can create a layout.js
file that will automatically wrap the content of the routes where it's placed:
// app/layout.js
import Header from '../components/Header';
import Footer from '../components/Footer';
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<Header />
<main>{children}</main>
<Footer />
</body>
</html>
);
}
Nested Layouts
One of the powerful features of layout components is the ability to nest them for different sections of your application.
Example of Nested Layouts with App Router
// app/layout.js (Root layout)
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<header>Main Site Header</header>
{children}
<footer>Main Site Footer</footer>
</body>
</html>
);
}
// app/dashboard/layout.js (Dashboard layout)
export default function DashboardLayout({ children }) {
return (
<div className="dashboard-container">
<nav className="dashboard-nav">
<ul>
<li>Dashboard Home</li>
<li>Analytics</li>
<li>Settings</li>
</ul>
</nav>
<div className="dashboard-content">
{children}
</div>
</div>
);
}
// app/dashboard/page.js
export default function DashboardPage() {
return <h1>Dashboard Home</h1>;
}
In this example, any page under /dashboard
will have both the root layout and the dashboard-specific layout applied.
Conditional Layouts
Sometimes you might want to use different layouts for different pages or conditions:
// pages/_app.js
import DefaultLayout from '../components/DefaultLayout';
import AdminLayout from '../components/AdminLayout';
export default function MyApp({ Component, pageProps, router }) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout || ((page) => page);
// Or use conditional logic
const Layout = router.pathname.startsWith('/admin')
? AdminLayout
: DefaultLayout;
return (
<Layout>
{getLayout(<Component {...pageProps} />)}
</Layout>
);
}
Then in your page component, you can define a custom layout:
// pages/special-page.js
import SpecialLayout from '../components/SpecialLayout';
function SpecialPage() {
return <h1>This page has a special layout</h1>;
}
// Define a custom layout for this page
SpecialPage.getLayout = (page) => (
<SpecialLayout>{page}</SpecialLayout>
);
export default SpecialPage;
Real-World Example: E-commerce Site Layout
Let's create a more comprehensive example for an e-commerce site:
// components/layouts/ShopLayout.js
import { useState } from 'react';
import Navbar from '../Navbar';
import Footer from '../Footer';
import Sidebar from '../Sidebar';
import MobileMenu from '../MobileMenu';
export default function ShopLayout({ children }) {
const [sidebarOpen, setSidebarOpen] = useState(false);
return (
<div className="shop-container">
<Navbar onMenuClick={() => setSidebarOpen(true)} />
<div className="content-area">
<Sidebar
isOpen={sidebarOpen}
onClose={() => setSidebarOpen(false)}
className="desktop-sidebar"
/>
<MobileMenu
isOpen={sidebarOpen}
onClose={() => setSidebarOpen(false)}
className="mobile-only"
/>
<main className="product-content">
{children}
</main>
</div>
<Footer />
</div>
);
}
// app/shop/layout.js (Using App Router)
import ShopLayout from '../../components/layouts/ShopLayout';
export default function ShopPageLayout({ children }) {
return <ShopLayout>{children}</ShopLayout>;
}
Best Practices for Layout Components
- Keep them focused: Layouts should handle structure, not business logic
- Make them responsive: Ensure your layouts work well on different screen sizes
- Use composition: Break complex layouts into smaller, reusable components
- Consider performance: Only include what's necessary in layouts that wrap many pages
- Pass data properly: Use props or context for data that needs to be available across the layout
Layout Components with Data Fetching
Layouts can also incorporate data fetching for elements that should appear on multiple pages:
// app/blog/layout.js (Next.js 13+ App Router)
async function getCategoryList() {
const res = await fetch('https://api.example.com/categories', {
next: { revalidate: 3600 } // Revalidate every hour
});
return res.json();
}
export default async function BlogLayout({ children }) {
const categories = await getCategoryList();
return (
<div className="blog-layout">
<aside className="categories-sidebar">
<h3>Categories</h3>
<ul>
{categories.map(category => (
<li key={category.id}>
<a href={`/blog/category/${category.slug}`}>{category.name}</a>
</li>
))}
</ul>
</aside>
<main className="blog-content">
{children}
</main>
</div>
);
}
Summary
Layout components in Next.js provide a powerful way to maintain consistent UI structures across your application while keeping your code DRY and organized. They allow you to:
- Create reusable UI structures
- Nest layouts for different sections of your application
- Conditionally apply different layouts
- Handle responsive design patterns
- Fetch and provide data that's needed across multiple pages
By mastering layout components, you'll be able to build more maintainable and professional Next.js applications with consistent user experiences.
Additional Resources
Exercises
- Create a basic layout component with a header, footer, and sidebar
- Implement nested layouts for a dashboard section of your application
- Build a layout that changes based on the user's authentication status
- Create a mobile-responsive layout that shows a hamburger menu on small screens
- Implement a layout that fetches and displays global site data like categories or tags
Happy coding with Next.js layout components!
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)