Master SvelteKit, the full-stack framework for Svelte. Learn SSR, file-based routing, server functions, API routes, and build a complete production-ready e-commerce platform with real-time features.

SvelteKit is the official full-stack framework for Svelte, designed to make building web applications faster and easier. It combines Svelte's reactive compiler with server-side rendering, file-based routing, and API routes to create a complete development experience.
In this article, we'll explore SvelteKit's core concepts, understand why it exists, and build a complete production-ready e-commerce platform that demonstrates all fundamental SvelteKit patterns.
Before SvelteKit, Svelte developers faced challenges:
Svelte Labs created SvelteKit to solve these problems:
SvelteKit uses file-based routing similar to Next.js. Routes are defined by file structure.
<h1>Home Page</h1>
<p>This is the home page</p><h1>About Page</h1>
<p>This is the about page</p><script>
export let data;
</script>
<h1>Product: {data.product.name}</h1>SvelteKit renders pages on the server by default, improving performance and SEO.
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ fetch }) => {
const response = await fetch('/api/products');
const products = await response.json();
return {
products,
};
};Write backend logic directly in your routes using +page.server.ts and +server.ts files.
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async () => {
const products = [
{ id: 1, name: 'Product 1', price: 99.99 },
{ id: 2, name: 'Product 2', price: 149.99 },
];
return new Response(JSON.stringify(products), {
headers: { 'Content-Type': 'application/json' },
});
};
export const POST: RequestHandler = async ({ request }) => {
const data = await request.json();
// Save to database
return new Response(JSON.stringify({ success: true }), {
status: 201,
headers: { 'Content-Type': 'application/json' },
});
};Layouts wrap pages and share common UI across routes.
<script>
import Header from '$lib/components/Header.svelte';
import Footer from '$lib/components/Footer.svelte';
</script>
<Header />
<main>
<slot />
</main>
<Footer />
<style>
main {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
</style>Create REST API endpoints without leaving your SvelteKit project.
import type { RequestHandler } from './$types';
export const GET: RequestHandler = async ({ params }) => {
const userId = params.id;
// Fetch from database
const user = { id: userId, name: 'John Doe', email: 'john@example.com' };
return new Response(JSON.stringify(user), {
headers: { 'Content-Type': 'application/json' },
});
};
export const PUT: RequestHandler = async ({ params, request }) => {
const userId = params.id;
const data = await request.json();
// Update in database
return new Response(JSON.stringify({ success: true }), {
headers: { 'Content-Type': 'application/json' },
});
};
export const DELETE: RequestHandler = async ({ params }) => {
const userId = params.id;
// Delete from database
return new Response(null, { status: 204 });
};Handle requests before they reach your routes using hooks.
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
// Add custom headers
const response = await resolve(event);
response.headers.set('X-Custom-Header', 'value');
return response;
};Control page metadata for better SEO.
<script>
import { page } from '$app/stores';
</script>
<svelte:head>
<title>Home - My Store</title>
<meta name="description" content="Welcome to our online store" />
<meta property="og:title" content="Home - My Store" />
<meta property="og:description" content="Welcome to our online store" />
</svelte:head>
<h1>Welcome</h1>Handle errors gracefully with error pages.
<script>
import { page } from '$app/stores';
</script>
<h1>Error {$page.status}</h1>
<p>{$page.error?.message}</p>Manage configuration with environment variables.
VITE_API_URL=http://localhost:5173
DATABASE_URL=postgresql://user:password@localhost/dbname
SECRET_KEY=your-secret-keyimport { env } from '$env/dynamic/private';
import { PUBLIC_API_URL } from '$env/static/public';
export const load = async () => {
const apiUrl = PUBLIC_API_URL;
const dbUrl = env.DATABASE_URL;
return { apiUrl };
};Deploy SvelteKit to various platforms with adapters.
npm install -D @sveltejs/adapter-vercelimport adapter from '@sveltejs/adapter-vercel';
export default {
kit: {
adapter: adapter(),
},
};Let's build a complete e-commerce platform demonstrating all SvelteKit fundamentals.
ecommerce-app/
├── src/
│ ├── routes/
│ │ ├── +page.svelte
│ │ ├── +page.server.ts
│ │ ├── +layout.svelte
│ │ ├── products/
│ │ │ ├── +page.svelte
│ │ │ ├── +page.server.ts
│ │ │ └── [id]/
│ │ │ ├── +page.svelte
│ │ │ └── +page.server.ts
│ │ ├── cart/
│ │ │ ├── +page.svelte
│ │ │ └── +page.server.ts
│ │ ├── checkout/
│ │ │ ├── +page.svelte
│ │ │ └── +page.server.ts
│ │ └── api/
│ │ ├── products/
│ │ │ └── +server.ts
│ │ ├── cart/
│ │ │ └── +server.ts
│ │ └── orders/
│ │ └── +server.ts
│ ├── lib/
│ │ ├── components/
│ │ ├── stores/
│ │ └── db.ts
│ └── hooks.server.ts
├── svelte.config.js
├── vite.config.ts
└── package.jsonexport interface Product {
id: string;
name: string;
description: string;
price: number;
image: string;
stock: number;
}
export interface CartItem {
productId: string;
quantity: number;
price: number;
}
export interface Order {
id: string;
items: CartItem[];
total: number;
status: 'pending' | 'processing' | 'shipped' | 'delivered';
createdAt: Date;
}// Mock database - replace with real database
const products: Product[] = [
{
id: '1',
name: 'Laptop',
description: 'High-performance laptop',
price: 999.99,
image: '/products/laptop.jpg',
stock: 10,
},
{
id: '2',
name: 'Mouse',
description: 'Wireless mouse',
price: 29.99,
image: '/products/mouse.jpg',
stock: 50,
},
];
export async function getProducts() {
return products;
}
export async function getProductById(id: string) {
return products.find((p) => p.id === id);
}
export async function createOrder(items: CartItem[]) {
const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const order = {
id: Date.now().toString(),
items,
total,
status: 'pending' as const,
createdAt: new Date(),
};
return order;
}import type { RequestHandler } from './$types';
import { getProducts } from '$lib/db';
export const GET: RequestHandler = async () => {
const products = await getProducts();
return new Response(JSON.stringify(products), {
headers: { 'Content-Type': 'application/json' },
});
};<script lang="ts">
import type { Product } from '$lib/types';
export let product: Product;
function addToCart() {
// Handle add to cart
}
</script>
<div class="product-card">
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>{product.description}</p>
<div class="price">${product.price}</div>
<button on:click={addToCart}>Add to Cart</button>
</div>
<style>
.product-card {
border: 1px solid #ddd;
border-radius: 8px;
padding: 16px;
text-align: center;
}
img {
width: 100%;
height: 200px;
object-fit: cover;
border-radius: 4px;
}
.price {
font-size: 1.5rem;
font-weight: bold;
color: #333;
margin: 10px 0;
}
button {
background: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #0056b3;
}
</style><script lang="ts">
import ProductCard from '$lib/components/ProductCard.svelte';
import type { PageData } from './$types';
export let data: PageData;
</script>
<svelte:head>
<title>Products - Store</title>
</svelte:head>
<h1>Products</h1>
<div class="products-grid">
{#each data.products as product (product.id)}
<ProductCard {product} />
{/each}
</div>
<style>
.products-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 20px;
margin-top: 20px;
}
</style>import type { PageServerLoad } from './$types';
import { getProducts } from '$lib/db';
export const load: PageServerLoad = async () => {
const products = await getProducts();
return { products };
};<script>
import '../app.css';
</script>
<header>
<nav>
<a href="/">Home</a>
<a href="/products">Products</a>
<a href="/cart">Cart</a>
</nav>
</header>
<main>
<slot />
</main>
<footer>
<p>© 2026 E-Commerce Store</p>
</footer>
<style>
:global(body) {
margin: 0;
font-family: system-ui, -apple-system, sans-serif;
}
header {
background: #333;
color: white;
padding: 20px;
}
nav {
display: flex;
gap: 20px;
}
nav a {
color: white;
text-decoration: none;
}
nav a:hover {
text-decoration: underline;
}
main {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
footer {
background: #f5f5f5;
padding: 20px;
text-align: center;
margin-top: 40px;
}
</style>import { writable } from 'svelte/store';
import type { CartItem } from '$lib/types';
export const cart = writable<CartItem[]>([]);
export function addToCart(productId: string, quantity: number, price: number) {
cart.update((items) => {
const existing = items.find((item) => item.productId === productId);
if (existing) {
existing.quantity += quantity;
return items;
}
return [...items, { productId, quantity, price }];
});
}
export function removeFromCart(productId: string) {
cart.update((items) => items.filter((item) => item.productId !== productId));
}
export function clearCart() {
cart.set([]);
}import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
// Add authentication check
const token = event.cookies.get('auth_token');
if (token) {
event.locals.user = { authenticated: true };
}
const response = await resolve(event);
return response;
};FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "build"]version: '3.8'
services:
app:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://user:password@db:5432/ecommerce
depends_on:
- db
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=ecommerce
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:// ✅ Keep routes organized by feature
// ✅ Use $lib for shared code
// ✅ Separate server and client code
// ✅ Use TypeScript for type safety
// ✅ Keep components small and focused// ✅ Use server-side rendering for SEO
// ✅ Implement proper caching strategies
// ✅ Lazy load components when possible
// ✅ Optimize images and assets
// ✅ Use proper database indexing// ✅ Validate all user input
// ✅ Use CSRF protection
// ✅ Implement proper authentication
// ✅ Use environment variables for secrets
// ✅ Sanitize output to prevent XSS// ✅ Create custom error pages
// ✅ Log errors properly
// ✅ Return meaningful error messages
// ✅ Handle edge cases
// ✅ Test error scenarios// ✅ Test components with Vitest
// ✅ Test API routes
// ✅ Test user interactions
// ✅ Use integration tests
// ✅ Aim for high coverage// ❌ Wrong - database code in component
<script>
import { db } from '$lib/db';
let products = [];
onMount(async () => {
products = await db.getProducts();
});
</script>
// ✅ Correct - use +page.server.ts
// src/routes/+page.server.ts
export const load = async () => {
const products = await db.getProducts();
return { products };
};// ❌ Wrong - repeating header/footer
<script>
import Header from '$lib/Header.svelte';
import Footer from '$lib/Footer.svelte';
</script>
<Header />
<main>Content</main>
<Footer />
// ✅ Correct - use layout
// src/routes/+layout.svelte
<Header />
<slot />
<Footer />// ❌ Wrong - silent failures
export const load = async () => {
const data = await fetch('/api/data');
return { data };
};
// ✅ Correct - handle errors
export const load = async ({ fetch }) => {
try {
const response = await fetch('/api/data');
if (!response.ok) throw new Error('Failed to fetch');
const data = await response.json();
return { data };
} catch (error) {
throw error;
}
};// ❌ Wrong - no validation
export const POST: RequestHandler = async ({ request }) => {
const data = await request.json();
// Use data directly
};
// ✅ Correct - validate input
import { z } from 'zod';
const schema = z.object({
name: z.string().min(1),
email: z.string().email(),
});
export const POST: RequestHandler = async ({ request }) => {
const data = await request.json();
const validated = schema.parse(data);
// Use validated data
};// ❌ Wrong - no metadata
<h1>Product</h1>
// ✅ Correct - add metadata
<svelte:head>
<title>{product.name} - Store</title>
<meta name="description" content={product.description} />
<meta property="og:title" content={product.name} />
</svelte:head>
<h1>{product.name}</h1>SvelteKit provides a complete, modern framework for building full-stack web applications with Svelte. Its file-based routing, built-in SSR, and server functions make it an excellent choice for developers who want a unified development experience.
The e-commerce platform we built demonstrates all core SvelteKit concepts in action. By mastering these fundamentals and following best practices, you'll be able to build scalable, performant web applications.
Key takeaways:
Next steps:
SvelteKit is a powerful framework that makes full-stack development enjoyable and productive. Start building today!