Routing w Next.js - na czym polega i jak tworzyć routy?

Blog image - Routing w Next.js - na czym polega i jak tworzyć routy?

15/12/2023

9 min

Bartosz Lewandowski

Routing w Next.js - na czym polega i jak tworzyć routy?

15/12/2023

9 min

Bartosz Lewandowski

Blog image - Routing w Next.js - na czym polega i jak tworzyć routy?

Spis Treści

  1. Tworzenie Routów
  2. Struktura plików w folderze app
  3. Nawigacja
  4. Dynamiczne ścieżki (dynamic routes)
  5. Podsumowanie

Przeczytaj także: Czym jest Vercel i czy warto go wybrać? - Kilka słów od eksperta

Routing to jeden z najważniejszych aspektów każdej aplikacji internetowej. W tym artykule przyjrzymy się dokładnie, jak działa routing w Next.js (app router), nowej wersji popularnego frameworka. Czy zastanawiałeś się kiedyś, jak to jest, że klikając w link, przenosisz się do innej części strony internetowej bez konieczności przeładowania całej strony? To właśnie magia routingu! W Next.js, routing jest prostszy niż kiedykolwiek, ale co to dokładnie oznacza dla Ciebie, jako twórcy aplikacji?

Tworzenie Routów

Podstawowe routy: Każdy plik JavaScript, TypeScript, JSX, czy TSX w folderze app tworzy trasę. Na przykład, plik app/about/page.tsx odpowiada trasie /about.

Dynamiczne parametry: Aby utworzyć trasę z dynamicznymi parametrami, użyj nawiasów kwadratowych w nazwie pliku. Na przykład, app/posts/[id]/page.tsx będzie odpowiadać trasie takiej jak /posts/123.

Nested routes (Zagnieżdżone trasy): Możesz tworzyć zagnieżdżone trasy poprzez umieszczanie plików w podkatalogach. Na przykład, app/products/electronics/page.tsx odpowiada trasie /products/electronics.

Struktura plików w folderze app

W Next.js 13, "App Router" stanowi znaczącą zmianę w sposobie, w jaki deweloperzy organizują i zarządzają komponentami oraz trasami (routingiem) w swoich aplikacjach. Ta nowa koncepcja przynosi większą elastyczność i bardziej modułową architekturę, co pozwala na lepsze zarządzanie strukturą aplikacji i jej trasami.

Layout

Plik layout.tsx jest używany do definiowania wspólnego układu (layoutu) dla grupy tras. Może być użyty do określenia elementów, które będą wspólne dla wielu stron, takich jak nagłówek, stopka, pasek boczny, nawigacja itp.

Charakterystyka:

  1. Miejsce umieszczenia: Umieszczany jest w katalogu app lub w jego podkatalogach, aby definiować layouty dla poszczególnych sekcji aplikacji.
  2. Zagnieżdżanie: Możesz tworzyć zagnieżdżone layouty. Na przykład, jeśli masz layout.tsx w app/products, będzie on stosowany do wszystkich stron w app/products i jej podkatalogach.

Przykład:

import React from "react";
 
const Layout = ({ children }) => {
	return (
		<div>
			<header>Header Content</header>
			<main>{children}</main>
			<footer>Footer Content</footer>
		</div>
	);
};
export default Layout;
 

Template

Plik template.tsx w Next.js 13 pełni rolę podobną do layoutu (layout.tsx), ale jest używany w nieco inny sposób. Jego główną funkcją jest otaczanie każdego dziecka (child layout lub page) unikalną instancją, co oznacza, że jest on tworzony na nowo przy każdej nawigacji.

Charakterystyka:

  1. Nowa instancja na nawigację: Za każdym razem, gdy użytkownik nawiguje między trasami, które używają tego samego stanu, template.tsx jest montowany na nowo. Oznacza to, że elementy DOM są tworzone od nowa, stan nie jest zachowywany, a efekty są resynchronizowane.
  2. Wykorzystanie useEffect i useState: Jest to idealne miejsce na funkcje, które polegają na useEffect (np. logowanie wyświetleń stron) i useState (np. formularze opinii specyficzne dla każdej strony).

Przykład:

import React from "react";
 
const Template = ({ title, content }) => {
	return (
		<div>
			<h2>{title}</h2>
			<p>{content}</p>
		</div>
	);
};
export default Template;

Page

Plik page.tsx stanowi podstawę dla każdej indywidualnej strony w aplikacji Next.js.

Charakterystyka:

  1. Struktura URL: Nazwa i lokalizacja pliku page.tsx bezpośrednio definiuje ścieżkę URL dla danej strony.
  2. Logika strony: Może zawierać stan lokalny, obsługę zdarzeń, dynamiczne importy oraz inne logiki specyficzne dla danej strony.

Przykład:

import React from "react";
 
const ContactPage = () => {
	return (
		<div>
			<h1>Kontakt</h1>
			<p>Skontaktuj się z nami używając poniższego formularza.</p>
		</div>
	);
}
export default ContactPage;

Loading

Plik loading.tsx definiuje komponent wyświetlany podczas ładowania danych lub zawartości strony.

Charakterystyka:

  1. Ładowanie danych: Oferuje graficzną reprezentację ładowania danych, poprawiając wrażenia użytkownika.
  2. Uniwersalność: Może być stosowany w różnych kontekstach, np. przy ładowaniu strony, danych, obrazów.

Przykład:

import React from "react";
 
const Loading =()=> {
	return (
		<div className="loading-container">
			<p>Trwa ładowanie, proszę czekać...</p>
		</div>
	);
}
 
export default Loading;

Not Found

Plik not-found.tsx jest używany do definiowania komponentu, który jest wyświetlany, gdy użytkownik próbuje uzyskać dostęp do strony, która nie istnieje (błąd 404).

Charakterystyka:

  1. Komunikacja z użytkownikiem: Informuje użytkownika, że strona, której szuka, nie została znaleziona.
  2. Przyjazny interfejs: Zawiera zazwyczaj linki powrotne do strony głównej lub innych głównych sekcji witryny.

Przykład

import React from "react";
import Link from "next/link";
 
const NotFound = () =>{
	return (
		<div className="not-found-container">
			<h1>404 - Nie znaleziono strony</h1>
			<p>Nie możemy znaleźć strony, której szukasz.</p>
			<Link href="/">Wróć do strony głównej</Link>
		</div>
	);
}
 
export default NotFound;

Error

Plik error.tsx służy do tworzenia komponentu wyświetlanego w przypadku wystąpienia błędu w aplikacji.

Charakterystyka:

  1. Obsługa Błędów: Może obsługiwać różne rodzaje błędów, w tym błędy serwera czy błędy aplikacji.
  2. Informowanie użytkownika: Zawiera informacje o błędzie oraz możliwe sugestie dla użytkownika.

Przykład

import React from "react";
 
const  ErrorPage =({ statusCode }) =>{
	return (
		<div className="error-container">
			<h1>Błąd</h1>
			<p>Wystąpił błąd{statusCode ? `: ${statusCode}` : ""}.</p>
			<p>Przepraszamy za niedogodności.</p>
		</div>
	);
}
 
export default ErrorPage;

Nawigacja

W Next.js istnieją dwa sposoby nawigacji pomiędzy trasami (routes):

  1. Użycie komponentu <Link>
  2. Użycie hooka useRouter

Poniżej omówię, jak korzystać z <Link> i useRouter(), oraz przybliżę działanie nawigacji.

to wbudowany komponent, który rozszerza tag HTML <a>, oferując prefetching i nawigację po stronie klienta między trasami. Jest to główny sposób nawigacji między trasami w Next.js.Możesz użyć go, importując go z next/link i przekazując prop href do komponentu:

import Link from "next/link";
 
const Page =()=> {
	return <Link href="/dashboard">Dashboard</Link>;
}
 
export default Page;

PrzykładyLinkowanie do dynamicznych segmentów: Możesz używać literałów szablonów i interpolacji do generowania listy linków. Na przykład, aby wygenerować listę postów na blogu:

import Link from "next/link";
 
export default function Posts({ posts }) {
	return (
		<ul>
			{posts.map((post) => (
				<li key={post.id}>
					<Link href={`/blog/${post.slug}`}>{post.title}</Link>
				</li>
			))}
		</ul>
	);
}

Sprawdzanie aktywnych linków: Można użyć usePathname(), aby określić, czy link jest aktywny. Na przykład, aby dodać klasę do aktywnego linku, można sprawdzić, czy bieżąca ścieżka (pathname) pasuje do href linku:

import { usePathname } from "next/navigation";
import Link from "next/link";
export function LinkComponent() {
	const pathname = usePathname();
	return (
		<nav>
			<ul>
				<li>
					<Link className={`${pathname === "/" ? "active" : ""}`} href="/">
						Home
					</Link>
				</li>
			</ul>
		</nav>
	);
}
 

Przewijanie do id: Możesz chcieć przewinąć do konkretnego id przy nawigacji. W tym celu możesz dołączyć do URL # hash

<Link href="/marketplace#milk">Milk</Link>

Wyłączenie przewijania: Jeśli chcesz wyłączyć to zachowanie, możesz przekazać scroll={false} do komponentu <Link>

<Link href="/marketplace" scroll={false}>Marketplace</Link>

Hook useRouter() W Next.js istnieje możliwość bezpośredniego wykorzystania Routera, co może być przydatne w specyficznych sytuacjach, mimo że w większości przypadków korzystanie z komponentu <Link> jest preferowane. Użycie routera warto rozważyć w następujących okolicznościach:

  1. Automatyczne przekierowanie: Po wykonaniu asynchronicznej operacji, takiej jak zapytanie do API, może być konieczne automatyczne przekierowanie użytkownika na inną podstronę.
  2. Zmiana adresu dla niezalogowanych użytkowników: W sytuacji, gdy niezalogowany użytkownik próbuje uzyskać dostęp do zasobów ograniczonych, router może być użyty do zmiany adresu strony na bardziej odpowiedni.
  3. Nawigacja po zalogowaniu: Po pomyślnym procesie logowania, router może być wykorzystany do przekierowania użytkownika z powrotem na stronę, z której rozpoczął proces logowania.Obiekt router zawiera kilka przydatnych funkcji: push, replace, refresh, prefetch, back, forward

Przykład:

import { useRouter } from "next/navigation";
export default function Page() {
	const router = useRouter();
 
	const navigateToMarketplace = () => {
		router.push("/marketplace");
	};
 
	return <button onClick={navigateToMarketplace}>Marketplace</button>;
}

Hook useParams()

Ten hook dostarcza nam obiekt zawierający parametry dynamiczne, które są w istocie równoważne z tym, co znajdziemy w params przesyłanych do komponentu page.tsx.

Przykład:

import { useParams } from "react-router-dom";
 
function ProductPage() {
	const { productId } = useParams();
	return <div>Produkt ID: {productId}</div>;
}

Hook useSearchParams()

Ta funkcjonalność przekazuje coś w rodzaju searchParams z page.tsx, ale z zasadniczą różnicą. Zamiast zwykłego obiektu otrzymujemy instancję URLSearchParams, a ściślej ReadonlyURLSearchParams.

Przykład:

import { useSearchParams } from "react-router-dom";
function SearchPage() {
	const [searchParams] = useSearchParams();
	return <div>Wyszukiwane słowo: {searchParams.get("query")}</div>;
}

Jeśli adres URL to /search?query=telefon, komponent SearchPage wyświetli "Wyszukiwane słowo: telefon".

Hook usePathname()

Zwraca bieżącą ścieżkę.Przykład:

import { usePathname } from "react-router-dom";
function Navigation() {
	const pathname = usePathname();
	return <div>Aktualna ścieżka: {pathname}</div>;
}

Jeśli adres URL to /about, komponent Navigation wyświetli "Aktualna ścieżka: /about".

Hook useSelectedLayoutSegment()

Pozwala na odczytanie ścieżki pliku strony na poziomie głębszym niż komponent, w którym został wywołany. Choć brzmi to skomplikowanie, poniższy przykład powinien to wyjaśnić. Użycie tego hooka jest sensowne tylko w plikach layout.tsx.

Przykład:

import { useSelectedLayoutSegment } from "react-router-dom";
 
function DashboardLayout() {
	const segment = useSelectedLayoutSegment();
	return <div>Aktualny segment: {segment}</div>;
}

W tym przykładzie, jeśli użytkownik jest na stronie /dashboard/settings, DashboardLayout wyświetli "Aktualny segment: settings".

Hook useSelectedLayoutSegments()

Działa podobnie do useSelectedLayoutSegment, lecz zwraca tablicę ścieżek, stąd liczba mnoga w nazwie. Przyjrzyjmy się przykładowi dla lepszego zrozumienia: Przykład:

import { useSelectedLayoutSegments } from "react-router-dom";
 
function Breadcrumbs() {
	const segments = useSelectedLayoutSegments();
	return <div>Ścieżka: {segments.join(" / ")}</div>;
}

Jeśli adres URL to /dashboard/settings/profile, Breadcrumbs wyświetli "Ścieżka: dashboard / settings / profile".

Dynamiczne ścieżki (dynamic routes)

Next.js, popularny framework do tworzenia aplikacji, oferuje potężną funkcję dynamicznego routingu, która otwiera nowe możliwości dla developerów. Dynamiczne ścieżki w Next.js pozwalają na tworzenie bardziej interaktywnych i skalowalnych aplikacji internetowych.

Co to są dynamiczne ścieżki?

Dynamiczne ścieżki w Next.js pozwalają na tworzenie URL-i, które dopasowują się do danych wejściowych użytkownika lub specyficznych wymagań aplikacji. Dzięki temu możemy tworzyć bardziej elastyczne i przyjazne aplikacje.

Konwencja

Dynamiczny segment tworzy się przez umieszczenie nazwy folderu w nawiasach kwadratowych: [folderName]. Na przykład [id] lub [slug].

Te dynamiczne segmenty są przekazywane jako prop params do funkcji layout, page, route oraz generateMetadata.

Przykład: Przykładowo, blog może zawierać ścieżkę app/blog/[slug]/page.js, gdzie [slug] jest dynamicznym segmentem dla postów blogowych.

export default function Page({ params }: { params: { slug: string } }) {
	return <div>My Post: {params.slug}</div>;
}

Strony statyczne

Funkcja generateStaticParams służy statycznego generowania tras w czasie budowania aplikacji.

// app/blog/[slug]/page.tsx

export async function generateStaticParams() {
	const users = await fetch("https://.../users").then((res) => res.json());
	return users.map((user) => ({ slug: user.id }));
}
 

Zastosowania Dynamicznych Ścieżek

  1. Strony profili użytkowników: Jak wspomniano wyżej, dynamiczne ścieżki są idealne do tworzenia stron profilowych, gdzie każdy użytkownik ma unikalny URL oparty o jego ID.
  2. Blogi i artykuły: Podobnie, w serwisach informacyjnych lub blogach, dynamiczne ścieżki mogą być użyte do generowania indywidualnych stron dla każdego artykułu lub postu.
  3. Sklepy internetowe: W e-commerce dynamiczne ścieżki pozwalają na tworzenie oddzielnych stron dla każdego produktu, gdzie ścieżka zawiera unikalny identyfikator lub nazwę produktu.

Podsumowanie

W tym poście skupiam się na routingu w Next.js, ważnym aspekcie tworzenia aplikacji internetowych. Omówiłem podstawy tworzenia różnych rodzajów tras (routów) w Next.js, w tym standardowych, dynamicznych i zagnieżdżonych, a także struktury plików w folderze app. Zawarłem szczegółowe informacje o komponentach jak layout.tsx, template.tsx, page.tsx, oraz o obsłudze różnych sytuacji, jak strony ładowania, błędów, czy nieznalezionych stron. Ponadto, przewodnik przedstawia różne metody nawigacji, w tym użycie komponentu <Link>, różnych hooków oraz dynamicznych ścieżek. Praktyczne przykłady kodu są włączone do każdego tematu, aby lepiej zrozumieć ich zastosowanie.

Uffff... No to mamy to za sobą. Dalej głodny wiedzy? No to pora wgryźć się w bardzo ważny temat jakim jest rendering. Ale spokojnie, bo w artykule Renderowanie w Next.js - server i client components w pigułc wszystko dokładnie omówiliśmy! Sprawdź sekcję FAQ i do zobaczenia! :)

Często zadawane pytania

  1. Czym jest routing w Next.js? Routing w Next.js to sposób zarządzania nawigacją w aplikacji, pozwalający na ładowanie różnych komponentów bez konieczności przeładowania całej strony.
  2. Jak tworzy się podstawowe routy w Next.js? Podstawowe routy tworzy się, umieszczając pliki JavaScript, TypeScript, JSX, lub TSX w folderze app. Nazwa i lokalizacja pliku bezpośrednio definiuje trasę URL.
  3. Co to są dynamiczne ścieżki w Next.js? Dynamiczne ścieżki to te, które mogą się zmieniać w zależności od danych wejściowych użytkownika lub specyficznych wymagań aplikacji, umożliwiając tworzenie bardziej elastycznych tras.
  4. Jak działa nawigacja w Next.js? Nawigacja w Next.js może odbywać się za pomocą komponentu <Link> dla nawigacji po stronie klienta lub poprzez hooki, takie jak useRouter, do bardziej zaawansowanych zastosowań nawigacyjnych.
  5. Jakie są różnice między layout.tsx, template.tsx, a page.tsx? layout.tsx definiuje wspólny układ dla grupy tras, template.tsx tworzy unikalną instancję dla każdego dziecka przy nawigacji, a page.tsx stanowi podstawę dla każdej indywidualnej strony w aplikacji.
  6. Jak obsługuje się błędy i nieznalezione strony w Next.js? Używa się specjalnych plików, takich jak error.tsx i not-found.tsx, aby zdefiniować komponenty wyświetlane w przypadku błędów lub gdy strona nie zostanie znaleziona.
Bartosz Lewandowski

Bartosz Lewandowski

CEO

Newsletter