EthicsComputerCourses
Join training +91 90532 10052

Pages & Layouts

Next.js · Lesson 4 of 27 Intermediate 7 min read Updated

In the App Router, folders decide the URL and two special files decide what appears on the screen: page for the content of one route and layout for UI shared by many routes. Almost every Next.js feature builds on these two files.

In this lesson you will create pages, write the required root layout, nest layouts, see why a layout keeps its state when you move between pages, and meet the template file that does the opposite.

Pages make a route public

A route is a URL your app responds to. To create one, make a folder inside app and put a page.jsx (or .js, .tsx) file in it that default-exports a React component.

Text
app/
  layout.jsx          the root layout (required)
  page.jsx            /
  courses/
    page.jsx          /courses
    react/
      page.jsx        /courses/react
  contact/
    page.jsx          /contact
React JSX
// app/courses/page.jsx
export default function CoursesPage() {
  return (
    <>
      <h1>All courses</h1>
      <p>Pick a course and start learning today.</p>
    </>
  );
}

A folder without a page file is not a route. You can keep helper files such as CourseCard.jsx inside app/courses/ and they will never become URLs. Only the component exported from page (or from a route file, which you will meet in Route Handlers) is sent to the browser.

The root layout

A layout wraps pages with shared UI such as a header, navigation or footer. It receives a children prop, which is the page (or a nested layout) to show inside it.

The layout in the app folder itself is the root layout. It is required and it must contain the <html> and <body> tags, because Next.js does not add them for you.

React JSX
// app/layout.jsx
import "./globals.css";

export const metadata = {
  title: "Rohtak Code School",
  description: "Coding courses for school and college students.",
};

export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <header>
          <a href="/">Rohtak Code School</a>
        </header>
        <main>{children}</main>
        <footer>Open Monday to Saturday</footer>
      </body>
    </html>
  );
}

When someone visits /courses, Next.js renders RootLayout and passes CoursesPage as children. The metadata export sets the page <title>; you will learn it fully in Metadata & SEO.

Nested layouts

Layouts nest just like folders. Add a layout.jsx inside app/courses/ and it wraps every page under /courses, while still sitting inside the root layout:

React JSX
// app/courses/layout.jsx
export default function CoursesLayout({ children }) {
  return (
    <div className="two-columns">
      <aside>
        <h2>Categories</h2>
        <ul>
          <li>Web</li>
          <li>Data</li>
          <li>Mobile</li>
        </ul>
      </aside>
      <section>{children}</section>
    </div>
  );
}

For /courses/react the tree becomes: RootLayout > CoursesLayout > the page from app/courses/react/page.jsx. The /contact page gets only the root layout, because contact is outside the courses folder.

Layouts keep their state

On a client-side navigation (clicking a <Link>), Next.js swaps only the part that changed. Shared layouts are not rendered again: they stay mounted, keep their state and stay interactive. This is called partial rendering.

The runnable example below imitates this in plain React. The Layout component owns a search box. Switching pages changes only children, so the text you typed survives. Type a name, then click between the pages:

React JSXTry it yourself
import { useState } from "react";
import { createRoot } from "react-dom/client";

function Layout({ children }) {
  const [query, setQuery] = useState("");
  return (
    <div style={{ border: "2px solid #111827", padding: 12 }}>
      <label>
        Search in layout:{" "}
        <input id="search" value={query} onChange={(e) => setQuery(e.target.value)} />
      </label>
      <div style={{ marginTop: 12, background: "#f3f4f6", padding: 8 }}>{children}</div>
    </div>
  );
}

const pages = {
  home: <p>Home page: welcome, learners!</p>,
  courses: <p>Courses page: HTML, React, Next.js.</p>,
  contact: <p>Contact page: call the Rohtak office.</p>,
};

function App() {
  const [page, setPage] = useState("home");
  return (
    <>
      {Object.keys(pages).map((name) => (
        <button key={name} onClick={() => setPage(name)}>{name}</button>
      ))}
      <Layout>{pages[page]}</Layout>
    </>
  );
}

createRoot(document.getElementById("root")).render(<App />);

After typing "Priya" and clicking "courses", the box still says "Priya" and only the grey area changes. Real Next.js layouts behave the same way, which is why a sidebar that is scrolled halfway or a music player in a layout does not reset when you move between pages.

Templates remount on every navigation

Sometimes you want a fresh start on each page, for example to replay an entry animation or reset a feedback form. A template.jsx file has the same shape as a layout, but Next.js gives it a new key for every route, so React destroys and recreates it on each navigation.

React JSX
// app/courses/template.jsx
export default function CoursesTemplate({ children }) {
  return <div className="fade-in">{children}</div>;
}

Here is the difference in plain React. Giving a component a different key is exactly how a template forces a fresh copy:

React JSXTry it yourself
import { useState } from "react";
import { createRoot } from "react-dom/client";

function Note() {
  const [text, setText] = useState("");
  return <input id="note" placeholder="Type a note" value={text} onChange={(e) => setText(e.target.value)} />;
}

function App() {
  const [page, setPage] = useState("react");
  return (
    <>
      <button onClick={() => setPage(page === "react" ? "nextjs" : "react")}>Go to other page</button>
      <p>Current page: {page}</p>
      <h4>Like a layout (same key):</h4>
      <Note />
      <h4>Like a template (key changes with the page):</h4>
      <Note key={page} />
    </>
  );
}

createRoot(document.getElementById("root")).render(<App />);

Type in both boxes, then click the button. The first box keeps its text; the second one is empty again because React mounted a brand-new Note.

Common mistakes

  • Forgetting the default export. export function Page() without default is not picked up as the page. Use export default function.
  • Adding <html> or <body> to a nested layout. Only the root layout renders them. Nested layouts return ordinary elements such as <div> or <section>.
  • Wrong file name. The files must be named page and layout in lower case. Page.jsx or index.jsx does nothing in the App Router.
  • Expecting a layout to re-run on every navigation. Because layouts are not re-rendered, code inside them does not run again when the user moves to a sibling page. Put page-specific logic in the page.

Key points

  • A folder becomes a route only when it contains a page file with a default-exported component.
  • The root layout app/layout.jsx is required and must render <html> and <body>.
  • Layouts receive children and nest by folder, so a layout in app/courses wraps every page under /courses.
  • On navigation, shared layouts stay mounted and keep their state (partial rendering).
  • A template file looks like a layout but remounts on every navigation.

Exercise

Plan and write the files for a small school website with these URLs: /, /about, /notices and /notices/exams. Every page shows the school name in a header. Every page under /notices also shows a side menu with "Exams" and "Holidays". Write the root layout, the notices layout and the /notices/exams page.

Show solution
React JSX
// app/layout.jsx
export default function RootLayout({ children }) {
  return (
    <html lang="en">
      <body>
        <header><h1>Kochi Public School</h1></header>
        {children}
      </body>
    </html>
  );
}

// app/notices/layout.jsx
export default function NoticesLayout({ children }) {
  return (
    <div style={{ display: "flex", gap: 24 }}>
      <nav>
        <ul>
          <li>Exams</li>
          <li>Holidays</li>
        </ul>
      </nav>
      <section>{children}</section>
    </div>
  );
}

// app/notices/exams/page.jsx
export default function ExamsPage() {
  return <p>Half-yearly exams start on 2 December.</p>;
}

You also need app/page.jsx, app/about/page.jsx and app/notices/page.jsx. The notices layout wraps both /notices and /notices/exams, and the root layout wraps everything.

Quick Quiz

1. Which file makes the folder app/fees reachable at /fees?

2. What must the root layout contain?

3. A user types in a search box inside app/courses/layout.jsx, then moves from /courses/html to /courses/css. What happens to the text?

4. Which file remounts its content on every navigation?

Learn Next.js with a mentor

Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call