If you have spent the last few years bouncing between meta-frameworks, SvelteKit 2 will feel like a strange mix of relief and suspicion. Relief, because it actually removes complexity instead of adding it. Suspicion, because you keep waiting for the other shoe to drop. It never really does. Paired with Svelte 5, this stack is one of the most productive ways to ship a full-stack application in 2026, and the numbers back up the developer experience. Bundles come out roughly 35% smaller than what Svelte 4 produced. Routing, server functions, and authentication patterns are all first-class citizens, not plugins you duct-tape together.
The runes make reactivity explicit
The biggest mental shift comes from Svelte 5’s runes. Previous versions of Svelte used a $: label and a lot of compiler magic to track dependencies. It worked, but when something broke, you were debugging invisible wires. Runes replace that magic with explicit functions. You tell the compiler exactly what to track, and it listens.
Here is what you need to know.
- $state handles reactive variables. Wrap any value in
$state()and the compiler knows to watch it. - $derived computes values from other state. Need a filtered list or a formatted total? Use
$derived. The key difference from$effectis that$derivedis for values, not actions. - $effect runs side effects. Think document title updates, manual DOM measurements, or timers that need cleanup. It runs after the DOM commits, similar to a lifecycle hook but tied to specific reactive dependencies.
- $props replaces the old
export letpattern for receiving data in components. It is clearer, and it interacts better with TypeScript.
This model pays off in practice. Because the compiler only tracks what you mark, dead code stays dead. You stop wondering why a variable triggered an update, and you start trusting your own explicit instructions.
Routing by folders, not configuration
SvelteKit uses your file system for routing. There is no separate router file to maintain. Drop a +page.svelte file into a directory, and that directory becomes a live route.
Shared UI wraps those routes through +layout.svelte. Place one in your root, and every child route inherits it. Place one deeper in the tree, and only that section gets the wrapper.
Server-side logic lives in +page.server.ts. This runs before your page renders, so it is where you query a database, validate a cookie, or reject an unauthenticated user. The types flow automatically from your load function into your page component, which means your data is typed without writing manual interfaces.
Raw API endpoints go in +server.ts files. These export standard HTTP handlers—GET, POST, PUT, DELETE—so building a REST back end alongside your pages feels natural.
One underrated feature: group routes. By wrapping a folder name in parentheses, like (auth), you create a shared layout without adding a segment to the URL. This is perfect for login and signup pages that need the same stripped-down chrome but live at /login and /signup, not /auth/login.
Data, security, and progressive enhancement
Modern frameworks love to talk about full-stack, but many leave you guessing where to put auth checks or form logic. SvelteKit gives you clear hooks.
Use +page.server.ts for data fetching. The load function there runs exclusively on the server, so your database credentials never leak to the browser. SvelteKit generates types from your load return values, so your frontend stays honest.
Use hooks.server.ts to gate the whole application. This runs on every request, making it the right place to verify sessions, check JWT expiry, or attach user context to incoming events.
For mutations, use form actions. Instead of wiring up a separate API endpoint and handling JSON, you define an action inside +page.server.ts. The beauty here is progressive enhancement. If JavaScript fails to load—or if a user has it disabled—the form still submits to the server action and the page re-renders with the result. If JavaScript is present, SvelteKit enhances the experience without a full reload. You get resilience and polish from the same code.
One rule to keep in mind: do your math in $derived, not in $effect. Using $effect to calculate values can trigger update loops that are hard to trace. Keep $effect for true side effects, and let $derived own your computed state.
SvelteKit versus Next.js
Both frameworks can ship production applications, but the trade-offs are real.
Bundle size favors SvelteKit. Because Svelte compiles components to vanilla JavaScript and skips the Virtual DOM entirely, the runtime footprint stays small. Next.js carries React’s reconciliation engine with it.
Reactivity also differs. SvelteKit resolves runes at compile time. The browser receives plain updates. Next.js relies on React’s runtime hooks and reconciliation, which means more work happens on the client.
Onboarding is gentler with SvelteKit. The mental model is smaller. You are not juggling useEffect dependency arrays or memoization puzzles to avoid re-renders. TypeScript integration deserves a mention too. While both frameworks
