
SvelteKit Best Practices
SvelteKit has revolutionized the way we build web applications. This comprehensive guide covers best practices for routing, data loading, error handling, and more to help you build robust, scalable applications.
Project Structure
A well-organized project structure is crucial:
src/
├── lib/
│ ├── components/
│ ├── stores/
│ ├── utils/
│ └── types/
├── routes/
│ ├── api/
│ ├── (app)/
│ └── (marketing)/
└── app.html Routing Best Practices
Route Groups
Organize routes without affecting URLs:
routes/
├── (app)/
│ ├── dashboard/
│ └── settings/
└── (marketing)/
├── about/
└── pricing/ Advanced Layouts
Use layout inheritance effectively:
<!-- src/routes/(app)/+layout.svelte -->
<script>
import { page } from '$app/stores';
export let data;
</script>
<nav>
{#each data.navItems as item}
<a href={item.href} aria-current={$page.url.pathname === item.href ? 'page' : undefined}>
{item.label}
</a>
{/each}
</nav>
<slot /> Data Loading
Server Load Functions
Load data efficiently on the server:
// src/routes/blog/+page.server.ts
import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async ({ fetch, params }) => {
const response = await fetch("/api/posts");
const posts = await response.json();
return {
posts,
meta: {
title: "Blog Posts",
description: "Latest articles and tutorials",
},
};
}; Universal Load Functions
Share code between server and client:
// src/routes/posts/[slug]/+page.ts
import type { PageLoad } from "./$types";
export const load: PageLoad = async ({ fetch, params }) => {
const post = await fetch(`/api/posts/${params.slug}`).then((r) => r.json());
return {
post,
streamed: {
comments: fetch(`/api/posts/${params.slug}/comments`).then((r) =>
r.json(),
),
},
};
}; Form Actions
Progressive Enhancement
Build forms that work without JavaScript:
<!-- src/routes/contact/+page.svelte -->
<script>
import { enhance } from '$app/forms';
export let form;
</script>
<form method="POST" use:enhance>
<input name="email" type="email" required />
<textarea name="message" required></textarea>
{#if form?.success}
<p class="success">Message sent!</p>
{/if}
{#if form?.errors}
<p class="error">{form.errors.message}</p>
{/if}
<button type="submit">Send</button>
</form> // src/routes/contact/+page.server.ts
import type { Actions } from "./$types";
import { fail } from "@sveltejs/kit";
export const actions = {
default: async ({ request }) => {
const data = await request.formData();
const email = data.get("email");
const message = data.get("message");
if (!email || !message) {
return fail(400, { errors: { message: "All fields required" } });
}
await sendEmail(email, message);
return { success: true };
},
} satisfies Actions; Error Handling
Error Pages
Create custom error pages:
<!-- src/routes/+error.svelte -->
<script>
import { page } from '$app/stores';
</script>
<h1>{$page.status}: {$page.error?.message}</h1>
{#if $page.status === 404}
<p>This page doesn't exist!</p>
{:else}
<p>Something went wrong.</p>
{/if} Expected Errors
Handle expected errors gracefully:
import { error } from "@sveltejs/kit";
export const load: PageServerLoad = async ({ params }) => {
const post = await db.post.findUnique({
where: { slug: params.slug },
});
if (!post) {
throw error(404, {
message: "Post not found",
});
}
return { post };
}; API Routes
RESTful Endpoints
Build type-safe API routes:
// src/routes/api/posts/+server.ts
import type { RequestHandler } from "./$types";
import { json } from "@sveltejs/kit";
export const GET: RequestHandler = async ({ url }) => {
const limit = Number(url.searchParams.get("limit") ?? 10);
const posts = await db.post.findMany({ take: limit });
return json(posts);
};
export const POST: RequestHandler = async ({ request }) => {
const data = await request.json();
const post = await db.post.create({ data });
return json(post, { status: 201 });
}; Performance Optimization
Preload Data
Preload critical data:
<script>
import { preloadData } from '$app/navigation';
function preload(href) {
preloadData(href);
}
</script>
<a href="/posts" on:mouseenter={() => preload('/posts')}>
View Posts
</a> Lazy Loading
Load components on demand:
<script>
let HeavyComponent;
async function loadComponent() {
const module = await import('$lib/components/HeavyComponent.svelte');
HeavyComponent = module.default;
}
</script>
<button on:click={loadComponent}>Load Component</button>
{#if HeavyComponent}
<svelte:component this={HeavyComponent} />
{/if} State Management
Stores
Use Svelte stores for shared state:
// src/lib/stores/user.ts
import { writable } from "svelte/store";
function createUserStore() {
const { subscribe, set, update } = writable(null);
return {
subscribe,
login: (userData) => set(userData),
logout: () => set(null),
updateProfile: (updates) => update((user) => ({ ...user, ...updates })),
};
}
export const user = createUserStore(); Context API
Share data within component trees:
<!-- Parent.svelte -->
<script>
import { setContext } from 'svelte';
setContext('theme', {
primary: '#007bff',
secondary: '#6c757d'
});
</script>
<!-- Child.svelte -->
<script>
import { getContext } from 'svelte';
const theme = getContext('theme');
</script>
<style>
button {
background: v-bind('theme.primary');
}
</style> Security
CSRF Protection
SvelteKit includes built-in CSRF protection for form actions. Always use it:
// Automatically enabled for form actions
export const actions = {
default: async ({ request }) => {
// CSRF token is automatically validated
const data = await request.formData();
// Process form...
},
}; Content Security Policy
Configure CSP headers:
// src/hooks.server.ts
export const handle = async ({ event, resolve }) => {
const response = await resolve(event);
response.headers.set(
"Content-Security-Policy",
"default-src 'self'; script-src 'self' 'unsafe-inline'",
);
return response;
}; Testing
Unit Tests
Test your components:
import { render } from "@testing-library/svelte";
import Button from "./Button.svelte";
test("renders button with text", () => {
const { getByText } = render(Button, { props: { label: "Click me" } });
expect(getByText("Click me")).toBeInTheDocument();
}); Integration Tests
Test your routes:
import { expect, test } from "@playwright/test";
test("homepage loads", async ({ page }) => {
await page.goto("/");
await expect(page.locator("h1")).toContainText("Welcome");
}); Deployment
Adapters
Choose the right adapter for your platform:
// svelte.config.js
import adapter from "@sveltejs/adapter-auto"; // Auto-detects platform
export default {
kit: {
adapter: adapter(),
},
}; Environment Variables
Use environment variables securely:
// Access server-side only
import { env } from "$env/dynamic/private";
const apiKey = env.API_KEY;
// Access client-side (prefixed with PUBLIC_)
import { env } from "$env/dynamic/public";
const publicApiUrl = env.PUBLIC_API_URL; Conclusion
SvelteKit provides powerful features for building modern web applications. By following these best practices, you can create apps that are fast, maintainable, and delightful to work with.
Remember: Convention over configuration, but flexibility when you need it.
Browser support snapshot
Live support matrix for es6-module-dynamic-import from
Can I Use.
Show static fallback image

Source: caniuse.com









