Domů O mně Služby Portfolio Učím Blog Kontakt
← Zpět na učím

12 — Error handling (error.js / error.tsx)

Výuka

Proč to existuje?

V každé aplikaci můžou nastat chyby — server neodpoví, databáze není dostupná, uživatel nemá oprávnění, nebo prostě chyba v kódu. Bez ošetření chyb by aplikace "spadla" a uživatel by viděl prázdnou stránku nebo technickou chybovou hlášku.

Error handling umožňuje zobrazit uživatelsky přívětivou chybovou stránku a nabídnout možnost zotavení (např. tlačítko "Zkusit znovu").

Jak to funguje?

Next.js používá React Error Boundaries pro zachycení chyb. Když vytvoříš soubor error.js nebo error.tsx ve složce, automaticky obalí stránky v této složce error boundary.

Důležité:

  • error.js musí být client component (začíná "use client")
  • Zachytává chyby v page.js a vnořených komponentách
  • Nezachytává chyby v layout.js (na to slouží global-error.js v root)

Jak probíhá zpracování chyby:

  1. Nastane chyba v page nebo komponentě
  2. Next.js zachytí chybu pomocí error boundary
  3. Zobrazí se error.js místo původní stránky
  4. Uživatel může zkusit akci znovu (pomocí reset() funkce)

K čemu to slouží?

  • Lepší UX — uživatel vidí přátelskou hlášku místo technické chyby
  • Izolace chyb — chyba v jedné sekci nezničí celou aplikaci
  • Možnost zotavení — tlačítko "Zkusit znovu" může problém vyřešit
  • Per-route errors — každá sekce může mít vlastní error handling

JavaScript

// app/blog/error.js
"use client" // Error komponenty MUSÍ být client components!

export default function Error({ error, reset }) {
  return (
    <div style={{ padding: '2rem', textAlign: 'center' }}>
      <h2>Něco se pokazilo!</h2>
      <p style={{ color: '#666' }}>
        {error.message || 'Nepodařilo se načíst obsah'}
      </p>
      <button
        onClick={() => reset()}
        style={{
          padding: '0.5rem 1rem',
          marginTop: '1rem',
          cursor: 'pointer'
        }}
      >
        Zkusit znovu
      </button>
    </div>
  )
}

Příklad stránky, která může vyhodit chybu

// app/blog/page.js
export default async function BlogPage() {
  // Pokud API selže, error.js zachytí chybu
  const posts = await fetch('https://api.example.com/posts', {
    // Pokud server neodpoví, vyhodí se chyba
    cache: 'no-store'
  }).then(res => {
    if (!res.ok) throw new Error('Nepodařilo se načíst články')
    return res.json()
  })

  return (
    <div>
      <h1>Blog</h1>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
        </article>
      ))}
    </div>
  )
}

TypeScript

// app/blog/error.tsx
"use client"

interface ErrorProps {
  error: Error & { digest?: string }
  reset: () => void
}

export default function Error({ error, reset }: ErrorProps): JSX.Element {
  return (
    <div style={{ padding: '2rem', textAlign: 'center' }}>
      <h2>Něco se pokazilo!</h2>
      <p style={{ color: '#666' }}>
        {error.message || 'Nepodařilo se načíst obsah'}
      </p>
      <details style={{ marginTop: '1rem', textAlign: 'left' }}>
        <summary>Technické detaily</summary>
        <pre style={{ fontSize: '0.875rem', color: '#999' }}>
          {error.stack}
        </pre>
      </details>
      <button
        onClick={() => reset()}
        style={{
          padding: '0.5rem 1rem',
          marginTop: '1rem',
          cursor: 'pointer'
        }}
      >
        Zkusit znovu
      </button>
    </div>
  )
}

Typovaný příklad s fetch

// app/blog/page.tsx
interface Post {
  id: number
  title: string
  content: string
}

export default async function BlogPage(): Promise<JSX.Element> {
  const response = await fetch('https://api.example.com/posts', {
    cache: 'no-store'
  })

  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`)
  }

  const posts: Post[] = await response.json()

  return (
    <div>
      <h1>Blog</h1>
      {posts.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
        </article>
      ))}
    </div>
  )
}

Rozdíl JS vs TS

TypeScript přidává typy pro error prop (včetně volitelného digest pro produkční error tracking) a reset funkci. To zajišťuje, že error komponenta dostává správné props a můžeš bezpečně přistupovat k vlastnostem chyby.

Důležité poznámky

Props error komponenty

{
  error: Error & { digest?: string }  // Samotná chyba + digest pro tracking
  reset: () => void                   // Funkce pro znovunačtení
}
  • error.message — zpráva o chybě
  • error.stack — stack trace (užitečné pro debugging)
  • error.digest — unikátní hash (v produkci, pro error tracking služby)
  • reset() — funkce, která znovu vykreslí segment (zkusí načíst znovu)

Struktura error souborů

app/
├─ error.js            # zachytává chyby v celé aplikaci
├─ global-error.js     # zachytává chyby v root layoutu
├─ page.js
├─ blog/
  ├─ error.js         # zachytává chyby jen v /blog sekci
  └─ page.js
└─ dashboard/
   ├─ error.js         # zachytává chyby jen v /dashboard sekci
   └─ page.js

Global error handler

Pro zachycení chyb v root layoutu vytvoř global-error.js:

// app/global-error.js
"use client"

export default function GlobalError({ error, reset }) {
  return (
    <html>
      <body>
        <h2>Něco se hodně pokazilo!</h2>
        <button onClick={() => reset()}>Zkusit znovu</button>
      </body>
    </html>
  )
}

Pozor: global-error.js musí obsahovat vlastní a tagy!

Tip

  • Vždy začni s "use client" — error komponenty jsou klientské
  • Používej reset() pro obnovení — často to chybu vyřeší (např. dočasný výpadek API)
  • V produkci nezobrazuj stack trace uživateli — je to bezpečnostní riziko
  • Pro error tracking používej služby jako Sentry (použij error.digest)
  • Testy chyb: v development mode zkus záměrně vyhodit throw new Error("test")

Kvíz

Proč musí error.js komponenta začínat direktivou "use client"?

Error komponenty v Next.js používají React Error Boundaries, které vyžadují hooks jako getDerivedStateFromError a lifecycle metody dostupné pouze v client components. Proto musí každý error.js soubor začínat "use client" direktivou. Navíc funkce reset() je interaktivní (event handler), což také vyžaduje klientský kód.

🎯 Závěrečný projekt

Po dokončení všech 8 dílů vytvoříte jednoduchou Todо aplikaci v čistém JavaScriptu. Naučíte se, jak aplikovat vše, co jste se naučili, na reálný projekt.

Zobrazit podrobnosti projektu →

Připraveni začít?

Zaregistrujte se a získejte přístup ke všem dílům tohoto seriálu

Kontaktujte mě

Informace o seriálu

Obtížnost

Délka

Cca 480 minut

Počet videí

8 videí + projekty

Certifikát

Po dokončení obdržíte certifikát


Lekce v této sekci


Struktura lekcí (souborový strom)

06. Typescript specifika
  • v přípravě
08. Moduly tridy
  • v přípravě
09. React zaklady
  • v přípravě
10. React hooks
  • v přípravě
12. Nextjs server
  • v přípravě
13. Databaze auth
  • v přípravě
14. Nextjs pokrocile
  • v přípravě