We use cookies to enhance your experience on the site
CodeWorlds

Loading UI and layout.tsx

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.

What is layout.tsx?

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.

Root Layout - The Required Main Layout

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.

Nested Layouts

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

Dashboard 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.

Preserving Layout State During Navigation

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.

loading.tsx - Automatic Loading States

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.

How Does loading.tsx Work?

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:

  • The layout is displayed immediately (doesn't wait for page data)
  • The loading component appears automatically during loading
  • The page replaces the loading component when data is ready
  • The user sees a responsive interface instead of a blank screen

loading.tsx at Different Levels

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/

template.tsx vs layout.tsx

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
:

  • Entry/exit animations on pages (fade-in on every navigation)
  • Page view logging (useEffect with analytics on every entry)
  • Forms that should reset on navigation

Practical Applications

Navigation with Active State

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}

Summary

Layouts and loading UI are fundamental concepts of the App Router in Next.js 15:

  1. layout.tsx - a shared layout that wraps pages and is not re-rendered during navigation. It preserves component state.
  2. Root Layout - the required main layout with
    <html>
    and
    <body>
    tags.
  3. Nested layouts - each segment can have its own layout, creating a layout hierarchy.
  4. loading.tsx - an automatic loading indicator. Next.js wraps the page in
    <Suspense>
    with loading as the fallback.
  5. template.tsx - like a layout, but creates a new instance with each navigation. Ideal for animations and logging.

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.

Go to CodeWorlds