Next.js 14: Layouts vs Templates

Next.js 14: Layouts vs Templates

Crafting Seamless UI Experiences: Understanding Next.js Layouts and Templates

The new app router in Next.js introduced two new file conventions for building UI: layouts and templates. These help you organize your web pages, keep track of changes, and make your website run faster.

It's vital to know when and how to use each one to make your web applications work well and efficiently. This article will explain the differences between layouts and templates in an easy way, show you how to set them up, and suggest the best situations for using them.

Understanding layouts and templates

Layouts in Next.js act as persistent UI shells that wrap around the application's pages. They let developers define a common structure that stays intact across different routes without re-rendering. This is particularly useful for components like headers, footers, and sidebars that should remain consistent as users navigate through the app.

Templates in Next.js also act as UI shells that wrap around pages but with a key difference: a template is fully remounted every time a user navigates to a new page. This remounting process resets the component state and effects, providing a fresh start with each page transition.

Defining layouts and templates

To define a layout, create a React component in a file named layout.js or layout.tsx. This component should include a children prop to integrate child layouts or pages.

Here's a basic layout component example:

// app/layout.tsx

import Header from "./Header";
import Footer from "./Footer";

export default function Layout({ children }: { children: React.ReactNode }) {
  return (
    <div>
      <Header />
      {children}
      <Footer />
    </div>
  );
}

A template can be defined by exporting a default React component from a template.js or template.tsx file. Similar to a layout, the component should accept and render the children prop.

Here’s an example of a basic template component:

// app/template.tsx

"use client";
import { useEffect } from "react";

export default function Template({ children }: { children: React.ReactNode }) {
  useEffect(() => {
    console.log("Log page view");
  }, []);
  return <div>{children}</div>;
}

Tip: A single route can effectively contain both a layout and a template, with the layout serving as an outer shell that encases the template within it.

Component remounting: layouts versus templates

Let’s consider a minimal example to demonstrate component remounting when using layouts versus templates. Begin with a Next.js project initialized with create-next-app.

Initializing authentication interfaces

Inside the app folder, create an auth directory.

In the auth folder, add a login subdirectory with page.tsx to construct the login page:

export default function LoginPage() {
  return <h1>Login</h1>;
}

Create a register subdirectory with page.tsx for the registration page:

export default function RegisterPage() {
  return <h1>Register</h1>;
}

Implementing a shared layout

Still within the auth directory, craft a layout.tsx file that encompasses a user feedback input and navigation links:

"use client";
import Link from "next/link";
import { useState } from "react";

export default function AuthLayout({ children }) {
  const [feedback, setFeedback] = useState("");
  return (
    <div>
      <label htmlFor="feedback">Your UX Feedback</label>
      <input
        id="feedback"
        value={feedback}
        onChange={(e) => setFeedback(e.target.value)}
      />
      <nav>
        <Link href="/auth/login">Login</Link>
        <Link href="/auth/register">Register</Link>
      </nav>
      {children}
    </div>
  );
}

Toggle between the login and register routes and observe that the feedback input's state is maintained.

Transitioning to a template

Change the filename from layout.tsx to template.tsx. With this change, as you navigate between the login and register pages, the feedback input's state is reset, indicating that the component is remounted with each route change.

Choosing between layouts and templates

When developing a web application, you'll encounter scenarios where you need to decide between using layouts or templates. Here’s a guide to help you make an informed decision:

Opt for layouts to:

  • Ensure consistency. Use layouts to provide a uniform appearance and behavior across your application. They are ideal for common elements like headers and footers.

  • Maintain state. Layouts are beneficial for their ability to hold state and behaviors that should be persistent during navigation, such as user login status.

  • Boost performance. They minimize re-renders and streamline state management, which can lead to performance improvements.

Choose templates when you need:

  • Isolation. Templates are useful when components should not share state or behaviors, as they re-mount and re-render upon navigation.

  • Behavioral flexibility. If you need to trigger certain effects or state changes every time a user navigates to a component, templates provide this capability. For instance, they're suitable for:

  • Tracking page views with useEffect.

  • Collecting feedback with a useState managed form that's unique to each page.

  • To alter default behaviors. Templates can modify how certain features work within your framework. For example, they can control the display of fallback UIs in Suspense Boundaries during page transitions, which layouts cannot do.

In summary, default to layouts for their performance and state management advantages, especially for static and consistent elements across pages. On the other hand, templates are your go-to for creating distinct, state-independent instances of components where effects and state initialization should happen on each navigation.

Conclusion

In essence, Next.js layouts and templates offer distinct approaches for UI construction:

  • Layouts provide a stable UI framework that reduces re-renders and maintains state across navigation, ideal for consistent site-wide components.

  • Templates, by contrast, reset state and re-render with each page visit, suited for distinct, page-specific behavior.

Choose layouts for efficiency and consistency, and templates for isolated, state-independent components that require fresh state on navigation. The right choice hinges on the specific needs of each page within your Next.js application, balancing user experience with performance.