๐ก๏ธ Error Handling & SSR in Svelte: Your Appโs Safety Net
Analogy: Think of your Svelte app like a circus act. Error Boundaries are the safety nets that catch performers when they fall. Hydration is like bringing a statue to lifeโthe server sculpts it, and the browser breathes life into it!
๐ช The Big Picture
Imagine youโre watching a circus show. The acrobats are doing amazing flips (your components rendering). But what if someone slips? Without a safety net, the whole show stops!
In Svelte:
- Error Boundaries = Safety nets that catch falling components
- Hydration = The magical moment when a still statue starts moving
๐ฏ What Youโll Learn
graph TD A["Error Handling & SSR"] --> B["๐ก๏ธ Error Boundaries"] A --> C["๐ง Hydration"] B --> D["Catch errors gracefully"] B --> E["Show fallback UI"] C --> F["Server renders HTML"] C --> G["Browser makes it interactive"]
๐ก๏ธ Part 1: Error Boundaries
What Are Error Boundaries?
Simple Answer: A special wrapper that catches errors from its children and shows something helpful instead of crashing everything.
๐ Kid-Friendly Example
Imagine you have a toy box (your app). Inside are many toys (components). One toy is broken and makes a loud noise when you touch it (an error).
Without Error Boundary:
- Touch broken toy โ CRASH! All toys stop working!
With Error Boundary:
- Touch broken toy โ Only that toy is put aside
- A sign appears: โThis toy is being fixed!โ
- All other toys keep working!
How Error Boundaries Work in Svelte
In SvelteKit, you use a special file called +error.svelte:
<!-- +error.svelte -->
<script>
import { page } from '$app/stores';
</script>
<div class="error-box">
<h1>Oops! ๐
</h1>
<p>Error: {$page.error.message}</p>
<a href="/">Go Home</a>
</div>
๐ File Structure
src/routes/
โโโ +page.svelte (Your page)
โโโ +error.svelte (Catches errors!)
โโโ +layout.svelte (Wraps everything)
Creating Custom Error Boundaries
For component-level errors, create your own boundary:
<!-- ErrorBoundary.svelte -->
<script>
let hasError = false;
let errorMessage = '';
// Catch errors from children
export function handleError(error) {
hasError = true;
errorMessage = error.message;
}
</script>
{#if hasError}
<div class="fallback">
<p>Something went wrong!</p>
<p>{errorMessage}</p>
<button onclick={() => hasError = false}>
Try Again
</button>
</div>
{:else}
<slot />
{/if}
Using Your Error Boundary
<ErrorBoundary>
<RiskyComponent />
</ErrorBoundary>
๐ Error Boundary Best Practices
| Do โ | Donโt โ |
|---|---|
| Wrap risky components | Wrap entire app in one |
| Show helpful messages | Show technical jargon |
| Provide recovery options | Leave users stuck |
| Log errors for debugging | Ignore error details |
Real-World Error Handling
<script>
import { onMount } from 'svelte';
let data = null;
let error = null;
let loading = true;
onMount(async () => {
try {
const res = await fetch('/api/data');
if (!res.ok) throw new Error('Failed!');
data = await res.json();
} catch (e) {
error = e.message;
} finally {
loading = false;
}
});
</script>
{#if loading}
<p>Loading... โณ</p>
{:else if error}
<div class="error">
<p>๐ข {error}</p>
<button onclick={() => location.reload()}>
Retry
</button>
</div>
{:else}
<div>{data}</div>
{/if}
๐ง Part 2: Hydration
What Is Hydration?
Simple Answer: The server sends HTML (like a frozen statue), and the browser โwakes it upโ to make it interactive!
๐ญ The Statue Story
- Server: Sculpts a beautiful marble statue (HTML)
- Browser receives: A perfect but lifeless statue
- Hydration happens: Magic breath brings it to life!
- Result: The statue can move, talk, respond!
graph TD A["๐ฅ๏ธ Server"] -->|Sends HTML| B["๐ Static Page"] B -->|Browser loads JS| C["โก Hydration"] C -->|Attaches listeners| D["๐ฎ Interactive App"]
How Hydration Works
Step 1: Server Renders HTML
// +page.server.js
export async function load() {
const data = await fetchData();
return { data };
}
Step 2: HTML Arrives at Browser
<!-- What user sees immediately -->
<div>
<h1>Welcome!</h1>
<button>Click me</button>
</div>
<!-- Button exists but doesn't work yet! -->
Step 3: JavaScript Hydrates
<!-- +page.svelte -->
<script>
export let data;
function handleClick() {
alert('Now I work!');
}
</script>
<h1>Welcome!</h1>
<button onclick={handleClick}>
Click me
</button>
<!-- Now the button actually works! -->
Why Hydration Matters
Without Hydration (Client-Only)
User visits โ Blank page โ JS loads โ Content appears
โฑ๏ธ User waits... waits... waits...
๐ Bad experience!
With Hydration (SSR)
User visits โ Content appears INSTANTLY โ JS loads โ Becomes interactive
โฑ๏ธ User sees content immediately!
๐ Great experience!
Hydration Challenges
โ ๏ธ The Mismatch Problem
Server and client must produce the same HTML!
Bad Example:
<script>
// Different on server vs client!
let time = new Date().toLocaleTimeString();
</script>
<p>Time: {time}</p>
<!-- Server: "10:30:00 AM" -->
<!-- Client: "10:30:01 AM" -->
<!-- MISMATCH! Hydration error! -->
Good Example:
<script>
import { onMount } from 'svelte';
import { browser } from '$app/environment';
let time = '';
onMount(() => {
// Only runs in browser!
time = new Date().toLocaleTimeString();
});
</script>
<p>Time: {time || 'Loading...'}</p>
Handling Browser-Only Code
<script>
import { browser } from '$app/environment';
import { onMount } from 'svelte';
let windowWidth = 0;
onMount(() => {
if (browser) {
windowWidth = window.innerWidth;
}
});
</script>
{#if browser}
<p>Window width: {windowWidth}px</p>
{:else}
<p>Measuring window...</p>
{/if}
Partial Hydration Tips
Sometimes you donโt need everything to be interactive:
<!-- Static content - no JS needed -->
<header>
<h1>My Blog</h1>
<nav>Links here</nav>
</header>
<!-- Interactive part - needs hydration -->
<main>
<CommentSection />
<LikeButton />
</main>
๐ฏ Hydration Quick Rules
| Concept | Remember |
|---|---|
| SSR | Server makes HTML first |
| Hydration | Browser adds interactivity |
| Mismatch | Server + Client must match! |
onMount |
Runs only in browser |
browser |
Check if in browser |
๐ Putting It All Together
<script>
import { browser } from '$app/environment';
import { onMount } from 'svelte';
import { page } from '$app/stores';
let mounted = false;
let error = null;
onMount(() => {
mounted = true;
});
function riskyOperation() {
try {
// Do something risky
} catch (e) {
error = e.message;
}
}
</script>
{#if error}
<!-- Error Boundary fallback -->
<div class="error-state">
<p>๐ข {error}</p>
<button onclick={() => error = null}>
Try Again
</button>
</div>
{:else}
<!-- Hydration-safe content -->
{#if mounted}
<InteractiveWidget />
{:else}
<div class="skeleton">Loading...</div>
{/if}
{/if}
๐ช Circus Finale: Summary
graph TD A["Your Svelte App"] --> B{Error Occurs?} B -->|Yes| C["๐ก๏ธ Error Boundary Catches"] C --> D["Shows Fallback UI"] D --> E["User Can Recover"] B -->|No| F["๐ง Hydration Process"] F --> G["Server Renders HTML"] G --> H["Browser Receives Page"] H --> I["JS Loads & Hydrates"] I --> J["๐ฎ Fully Interactive!"]
๐ Key Takeaways
- Error Boundaries = Safety nets for your components
- Use
+error.sveltefor page-level errors in SvelteKit - Hydration = Server HTML + Browser JS = Interactive App
- Always match server and client rendering
- Use
onMountfor browser-only code - Check
browserbefore using window/document
๐ฏ Remember This!
Error Boundaries: โIf a clown falls, the show goes on!โ
Hydration: โThe server paints the picture, the browser brings it to life!โ
Youโve got this! Your Svelte apps are now crash-proof and lightning-fast! ๐
