Пошук уроків, статей та іншого контенту
Налаштуєте програмні перенаправлення та обробку відсутніх ресурсів за допомогою redirect() і notFound().
redirect() і notFound()У Next.js з App Router програмні перенаправлення та обробку відсутніх ресурсів можна виконувати без ручного формування HTTP-відповідей.
redirect() перенаправляє користувача на інший маршрут.
notFound() повідомляє Next.js, що ресурс не знайдено, і відображає сторінку not-found.tsx.
Обидві функції імпортуються з next/navigation:
import { redirect, notFound } from 'next/navigation'Ці функції призначені переважно для:
Server Components;
Server Actions;
Route Handlers.
redirect()redirect() приймає шлях, на який потрібно перенаправити користувача:
import { redirect } from 'next/navigation'
export default function LegacyPage() {
redirect('/products')
return null
}Після виклику redirect() виконання цього компонента припиняється. Тому код після нього зазвичай не виконується, а return null у прикладі потрібен лише для відповідності типам.
Перенаправлення може вести на:
внутрішній маршрут:
redirect('/dashboard')динамічний маршрут:
redirect(`/products/${productId}`)зовнішню адресу:
redirect('https://example.com')Припустімо, у застосунку більше не використовується маршрут /old-products. Його можна перенаправити на нову сторінку.
Файл app/old-products/page.tsx:
import { redirect } from 'next/navigation'
export default function OldProductsPage() {
redirect('/products')
}Тепер під час відкриття /old-products Next.js перенаправить користувача на /products.
За замовчуванням redirect() використовує тимчасове перенаправлення. Це означає, що старий маршрут усе ще може бути доступним у майбутньому.
Якщо перенаправлення має бути постійним, використовуйте permanentRedirect():
import { permanentRedirect } from 'next/navigation'
export default function OldProductPage() {
permanentRedirect('/products')
}Постійне перенаправлення має HTTP-статус 308. Воно підходить, наприклад, для остаточно змінених URL.
Програмне перенаправлення часто залежить від даних або стану користувача.
import { redirect } from 'next/navigation'
type User = {
isAuthenticated: boolean
}
async function getUser(): Promise<User> {
return {
isAuthenticated: false,
}
}
export default async function DashboardPage() {
const user = await getUser()
if (!user.isAuthenticated) {
redirect('/login')
}
return <h1>Dashboard</h1>
}Якщо користувач не автентифікований, він не побачить вміст DashboardPage, а буде перенаправлений на /login.
Перевірку потрібно виконати до повернення основного JSX.
redirect() у Server ActionУ Server Action redirect() часто використовують після успішного створення або оновлення даних.
'use server'
import { redirect } from 'next/navigation'
export async function createProduct(formData: FormData) {
const name = formData.get('name')
if (typeof name !== 'string' || name.trim() === '') {
throw new Error('Назва товару є обов’язковою')
}
const productId = 'new-product-id'
redirect(`/products/${productId}`)
}Після завершення Server Action користувач перейде на сторінку створеного товару.
У Server Action Next.js зазвичай використовує статус 303 See Other, оскільки перенаправлення часто виконується після POST-запиту.
notFound()Якщо динамічний маршрут отримав ідентифікатор, для якого немає даних, не варто повертати порожню сторінку або викликати redirect('/').
Для таких випадків використовується notFound():
import { notFound } from 'next/navigation'
export default async function ProductPage() {
const product = null
if (!product) {
notFound()
}
return <h1>Товар</h1>
}notFound():
припиняє виконання поточного компонента;
формує відповідь із HTTP-статусом 404;
відображає найближчий файл not-found.tsx.
not-found.tsxЩоб налаштувати інтерфейс сторінки 404, створіть файл not-found.tsx.
Файл app/not-found.tsx:
import Link from 'next/link'
export default function NotFound() {
return (
<main>
<h1>Сторінку не знайдено</h1>
<p>Запитуваний ресурс не існує або був видалений.</p>
<Link href="/">Повернутися на головну</Link>
</main>
)
}Файл у корені app може використовуватися для випадків, коли notFound() викликається в дочірніх маршрутах цього дерева.
Також можна створити локальний файл not-found.tsx усередині сегмента маршруту. Тоді він використовуватиметься для цього сегмента та його дочірніх маршрутів.
Розглянемо каталог товарів. Дані для прикладу зберігаються в масиві.
Файл app/products/[id]/page.tsx:
import { notFound } from 'next/navigation'
type Product = {
id: string
name: string
description: string
}
const products: Product[] = [
{
id: '1',
name: 'Механічна клавіатура',
description: 'Клавіатура з механічними перемикачами.',
},
{
id: '2',
name: 'Бездротова миша',
description: 'Миша з підключенням через Bluetooth.',
},
]
type ProductPageProps = {
params: Promise<{
id: string
}>
}
export default async function ProductPage({
params,
}: ProductPageProps) {
const { id } = await params
const product = products.find((item) => item.id === id)
if (!product) {
notFound()
}
return (
<main>
<h1>{product.name}</h1>
<p>{product.description}</p>
</main>
)
}Для URL /products/1 буде знайдено товар і відображено його дані.
Для URL /products/999 товар не буде знайдено, тому виконається notFound(), а користувач побачить app/not-found.tsx.
Можна додати окрему сторінку для товарів:
Файл app/products/not-found.tsx:
import Link from 'next/link'
export default function ProductNotFound() {
return (
<main>
<h1>Товар не знайдено</h1>
<p>Перевірте ідентифікатор товару або перегляньте каталог.</p>
<Link href="/products">Перейти до каталогу</Link>
</main>
)
}Тепер виклик notFound() у app/products/[id]/page.tsx використовуватиме app/products/not-found.tsx, а не загальний app/not-found.tsx.
Це дає змогу показувати контекстні повідомлення для різних частин застосунку.
redirect() і notFound()| Функція | Призначення | Типовий результат | |---|---|---| | redirect() | Ресурс доступний за іншою адресою | Перехід на інший маршрут | | permanentRedirect() | Ресурс назавжди переміщено | Постійне перенаправлення | | notFound() | Ресурс не існує | Сторінка 404 |
Вибирайте notFound(), якщо запитуваний ресурс не існує. Не використовуйте redirect() як заміну сторінці 404, якщо користувач не має іншого коректного маршруту.
redirect() і notFound() завершують обробку поточного маршруту. Їх не потрібно обгортати в return:
if (!product) {
notFound()
}Такий запис також можливий:
if (!user) {
redirect('/login')
}Перенаправлення має виконуватися до того, як маршрут завершить формування відповіді:
import { redirect } from 'next/navigation'
export default async function Page() {
const shouldRedirect = true
if (shouldRedirect) {
redirect('/new-page')
}
return <h1>Сторінка</h1>
}Якщо шлях формується з даних користувача, не дозволяйте безпосередньо передавати довільні зовнішні адреси. Інакше можна створити небезпечне перенаправлення на сторонній сайт.
Надійніше перенаправляти на заздалегідь визначені внутрішні маршрути:
import { redirect } from 'next/navigation'
const allowedRoutes = {
success: '/success',
failure: '/failure',
} as const
export default function ResultPage({
searchParams,
}: {
searchParams: { result?: string }
}) {
if (searchParams.result === 'success') {
redirect(allowedRoutes.success)
}
redirect(allowedRoutes.failure)
}notFound()if (!product) {
return null
}Так користувач отримає порожню сторінку, а сервер може повернути статус 200. Для відсутнього ресурсу використовуйте:
if (!product) {
notFound()
}redirect() для відсутнього ресурсуif (!product) {
redirect('/products')
}Це приховує факт помилки URL від користувача та пошукових систем. Якщо товар не існує, коректніше викликати notFound().
redirect()redirect('/login')
console.log('Цей код не має виконуватися')Після перенаправлення поточна обробка маршруту припиняється. Розміщуйте всю необхідну логіку до виклику redirect().
Правильний імпорт для App Router:
import { redirect, notFound } from 'next/navigation'Не імпортуйте ці функції з next/router.
redirect() виконує програмне перенаправлення на інший маршрут.
permanentRedirect() використовується для постійно переміщених ресурсів.
notFound() завершує обробку маршруту зі статусом 404.
Файл not-found.tsx визначає інтерфейс сторінки відсутнього ресурсу.
Файл not-found.tsx може бути глобальним або локальним для окремого сегмента.
Для неіснуючого ресурсу використовуйте notFound(), а не перенаправлення на випадкову сторінку.