In the Quantum Metropolis, every district has permanent infrastructure - lighting, navigation systems, information panels - that remains active regardless of who is currently in the zone. When a resident moves between sectors, holographic screens display a loading animation while the surrounding infrastructure remains unchanged.
In Next.js 15 App Router, these concepts have their equivalents: layout.tsx is the permanent infrastructure that wraps pages and is not re-rendered during navigation, and loading.tsx is the automatic loading indicator displayed during page transitions.
The
layout.tsx file is one of the most important special files in the App Router. It defines a shared layout that wraps pages in a given segment and all its subsegments. The most important feature of a layout: it is not re-rendered when navigating between pages that share the same layout. It's like the infrastructure of a Quantum district - it stays in place, only the content inside changes.Every Next.js application must have a Root Layout in the
app/layout.tsx file. This is the only layout that must contain the <html> and <body> tags:1// app/layout.tsx — Root Layout
2export default function RootLayout({
3 children,
4}: {
5 children: React.ReactNode
6}) {
7 return (
8 <html lang="pl">
9 <body>
10 <nav className="quantum-nav">
11 <a href="/">Home</a>
12 <a href="/dashboard">Dashboard</a>
13 <a href="/settings">Settings</a>
14 </nav>
15 <main>{children}</main>
16 <footer>Metropolis Quantum © 2150</footer>
17 </body>
18 </html>
19 );
20}The Root Layout is rendered once and remains active throughout the application's lifetime. Navigation between
/dashboard and /settings will only change the {children} content - the navigation and footer will stay in place without re-rendering.Layouts can be nested at any level of the folder structure. Each path segment can have its own
layout.tsx:1app/
2 ├── layout.tsx # Root Layout (html + body + navigation)
3 ├── page.tsx # Home /
4 └── dashboard/
5 ├── layout.tsx # Dashboard Layout (sidebar + panel)
6 ├── page.tsx # /dashboard
7 ├── analytics/
8 │ └── page.tsx # /dashboard/analytics
9 └── settings/
10 └── page.tsx # /dashboard/settingsDashboard Layout nests inside the Root Layout:
1// app/dashboard/layout.tsx — Nested layout
2export default function DashboardLayout({
3 children,
4}: {
5 children: React.ReactNode
6}) {
7 return (
8 <div className="dashboard-container">
9 <aside className="sidebar">
10 <h2>Quantum Panel</h2>
11 <ul>
12 <li><a href="/dashboard">Overview</a></li>
13 <li><a href="/dashboard/analytics">Analytics</a></li>
14 <li><a href="/dashboard/settings">Settings</a></li>
15 </ul>
16 </aside>
17 <section className="content">
18 {children}
19 </section>
20 </div>
21 );
22}When the user navigates from
/dashboard to /dashboard/analytics, the Root Layout and Dashboard Layout are not re-rendered. Only the {children} in the Dashboard Layout changes. This is a crucial performance optimization - the state of layout components (e.g., open sidebar, scroll position) is preserved.One of the greatest advantages of layouts is state preservation during navigation. Imagine a control panel in the Quantum Metropolis - when you switch between sections, you don't want the entire interface to reload:
1// app/dashboard/layout.tsx
2'use client';
3
4import { useState } from 'react';
5
6export default function DashboardLayout({
7 children,
8}: {
9 children: React.ReactNode
10}) {
11 // This state WILL SURVIVE navigation between /dashboard/* pages!
12 const [sidebarOpen, setSidebarOpen] = useState(true);
13
14 return (
15 <div className="flex">
16 {sidebarOpen && (
17 <aside className="w-64 bg-gray-900 p-4">
18 <h2>Quantum Panel</h2>
19 <nav>
20 <a href="/dashboard">Overview</a>
21 <a href="/dashboard/analytics">Analytics</a>
22 </nav>
23 </aside>
24 )}
25 <div className="flex-1">
26 <button onClick={() => setSidebarOpen(!sidebarOpen)}>
27 {sidebarOpen ? 'Hide' : 'Show'} sidebar
28 </button>
29 {children}
30 </div>
31 </div>
32 );
33}If the user closes the sidebar on the
/dashboard page and navigates to /dashboard/analytics, the sidebar will remain closed - the sidebarOpen state is not reset.The
loading.tsx file is a special file that Next.js automatically displays as a fallback while page content is loading. It's like a holographic loading screen in the Quantum Metropolis - it appears automatically when the system is preparing data.1// app/dashboard/loading.tsx
2export default function DashboardLoading() {
3 return (
4 <div className="loading-screen">
5 <div className="spinner"></div>
6 <p>Loading Quantum panel data...</p>
7 </div>
8 );
9}Next.js automatically wraps
page.tsx in a <Suspense> boundary, using loading.tsx as the fallback:1// This is what Next.js does "under the hood":
2<Layout>
3 <Suspense fallback={<Loading />}>
4 <Page />
5 </Suspense>
6</Layout>Thanks to this:
Each segment can have its own
loading.tsx:1app/
2 ├── loading.tsx # Loading for the home page
3 ├── dashboard/
4 │ ├── loading.tsx # Loading for /dashboard/*
5 │ ├── analytics/
6 │ │ └── loading.tsx # Analytics-specific loading
7 │ └── settings/
8 │ └── page.tsx # Will use loading from /dashboard/Next.js offers one more special file -
template.tsx. It looks identical to layout.tsx, but has one crucial difference: the template is re-mounted (a new instance is created) with each navigation.1// app/dashboard/template.tsx
2// New instance with EVERY navigation!
3export default function DashboardTemplate({
4 children,
5}: {
6 children: React.ReactNode
7}) {
8 return (
9 <div className="animate-fadeIn">
10 {children}
11 </div>
12 );
13}When to use
template.tsx instead of layout.tsx?| Feature | layout.tsx | template.tsx | |-------|-----------|-------------| | Re-rendering | NO - preserves state | YES - new instance | | Component state | Preserved between navigations | Reset on navigation | | useEffect | Does not re-run | Runs on every navigation | | Entry animations | Do not repeat | Repeat on every entry |
Use cases for
template.tsx:1// app/dashboard/layout.tsx
2'use client';
3
4import Link from 'next/link';
5import { usePathname } from 'next/navigation';
6
7export default function DashboardLayout({
8 children,
9}: {
10 children: React.ReactNode
11}) {
12 const pathname = usePathname();
13
14 const links = [
15 { href: '/dashboard', label: 'Overview' },
16 { href: '/dashboard/analytics', label: 'Analytics' },
17 { href: '/dashboard/settings', label: 'Ustawienia' },
18 ];
19
20 return (
21 <div className="dashboard">
22 <nav className="dashboard-nav">
23 {links.map((link) => (
24 <Link
25 key={link.href}
26 href={link.href}
27 className={pathname === link.href ? 'active' : ''}
28 >
29 {link.label}
30 </Link>
31 ))}
32 </nav>
33 <main>{children}</main>
34 </div>
35 );
36}Layouts and loading UI are fundamental concepts of the App Router in Next.js 15:
<html> and <body> tags.<Suspense> with loading as the fallback.These mechanisms, inspired by the permanent infrastructure of the Quantum Metropolis districts, allow building efficient and responsive interfaces where the user never sees a blank screen, and navigation is smooth and instantaneous.