Skip to main content

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:

jsx
// 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 and Footer components
  • We define a Layout component that accepts children 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:

jsx
// 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>
);
}
jsx
// 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:

jsx
// 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:

jsx
// 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

jsx
// 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>
);
}
jsx
// 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>
);
}
jsx
// 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:

jsx
// 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:

jsx
// 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:

jsx
// 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>
);
}
jsx
// 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

  1. Keep them focused: Layouts should handle structure, not business logic
  2. Make them responsive: Ensure your layouts work well on different screen sizes
  3. Use composition: Break complex layouts into smaller, reusable components
  4. Consider performance: Only include what's necessary in layouts that wrap many pages
  5. 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:

jsx
// 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

  1. Create a basic layout component with a header, footer, and sidebar
  2. Implement nested layouts for a dashboard section of your application
  3. Build a layout that changes based on the user's authentication status
  4. Create a mobile-responsive layout that shows a hamburger menu on small screens
  5. 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! :)