The third time I scaffolded the same folder structure in a week, I noticed I hadn't opened the SvelteKit docs in months. This is a piece about why: what SvelteKit is, and why "small enough to remember" turns out to be the feature that matters.
That's the thing nobody puts on the marketing page. SvelteKit is small enough to hold in your head, and it stays that way. After a dozen or so client projects on it (brochure sites, a couple of portals, an e-commerce front end, a booking flow) I've stopped thinking of it as a framework I use and started thinking of it as the shape my projects come in.
This piece is the long version of why. What SvelteKit is, how the moving parts actually fit together, where it sits next to Next.js and Nuxt, and what a real project on it looks like a year later.
Svelte builds components. SvelteKit builds apps.
People conflate the two, and it causes confusion. Svelte is a component framework, the thing that turns <script>, markup and <style> into a working UI. On its own it doesn't know anything about URLs, servers, data fetching, page transitions, SEO, or how your site gets deployed. You can build a widget with Svelte alone. You cannot really build a website.
SvelteKit is the framework that wraps around Svelte and answers all of those questions. It is to Svelte what Next.js is to React or Nuxt is to Vue: the layer that takes you from "I have components" to "I have a website people can visit, that ranks, that deploys, that has a backend." Vite runs the build and the dev server underneath, which is why cold starts and hot reloads feel close to instant.
A useful way to hold it: Svelte is a language feature, SvelteKit is the application framework, Vite is the toolchain. You mostly think about the first two.
The three ideas that carry the weight
The compiler does the work. Svelte is a compiler, not a runtime library you ship. Your components are transformed at build time into small, direct JavaScript that touches the DOM without a virtual DOM layer in between. There is no framework runtime of any real size sent to the browser. A SvelteKit page is quick off the mark by default, not because you profiled it and trimmed it, but because there was never much to send. On the client sites I have shipped, the JavaScript budget is usually a rounding error next to the images.
Routing is the filesystem. Everything lives under src/routes. The folder structure is the URL structure, and a set of special filenames tell SvelteKit what each file does:
src/routes/
+layout.svelte -> wraps every page
+page.svelte -> the "/" page
about/
+page.svelte -> the "/about" page
blog/
+page.server.ts -> loads the list of posts
+page.svelte -> renders the list
[slug]/
+page.server.ts -> loads one post
+page.svelte -> renders one post
api/
contact/
+server.ts -> POST endpoint at "/api/contact"
Once you have built three or four routes, you can read the entire architecture of an application straight from the file tree. New developers do not need a tour. The tree is the tour.
Rendering is a per-route decision. The same project can prerender the marketing pages to static HTML at build time, server-render the dashboard on every request, and hand a few highly interactive pages entirely to the client. You set it with a one-line export:
// src/routes/about/+page.ts
export const prerender = true;
Change your mind later and it is one line, not a migration. This is the part that quietly saves the most time over a project's life, because the rendering decision you make on day one is almost never the one you want on day ninety.
Data loading
Every page and layout can export a load function. A server load lives in +page.server.ts, runs only on the server, and is the right place for database calls, secrets, and anything you do not want in the browser bundle:
// src/routes/blog/+page.server.ts
import { getPosts } from '$lib/server/sanity';
export async function load() {
const posts = await getPosts();
return { posts };
}
The return value arrives in the component as a typed data prop:
<!-- src/routes/blog/+page.svelte -->
<script>
let { data } = $props();
</script>
{#each data.posts as post}
<a href="/blog/{post.slug}">{post.title}</a>
{/each}
No client-side fetch waterfall on first load. No separate data-fetching library. No hand-written loading state for the initial render, because the HTML arrives with the data already in it. When the user navigates client-side, SvelteKit re-runs only the load functions that actually depend on what changed.
There is a universal load too (+page.ts, no .server), which runs on the server for the first hit and then on the client for subsequent navigations. You reach for it less often, but it is there when you need data fetched from a public API on both sides.
Forms that work without JavaScript
This is the feature I miss most when I go back to other stacks. You define a form action in +page.server.ts:
// src/routes/contact/+page.server.ts
export const actions = {
default: async ({ request }) => {
const data = await request.formData();
await sendEmail(data.get('email'), data.get('message'));
return { success: true };
}
};
Point a plain HTML form at it and you are done:
<form method="POST">
<input name="email" type="email" />
<textarea name="message"></textarea>
<button>Send</button>
</form>
That form submits and works with JavaScript disabled, on a slow connection, before hydration finishes. Add SvelteKit's use:enhance and the same form upgrades in place to an async submission with no full page reload, no extra API route, and no client-side form library. Progressive enhancement is the default path here, not a badge you earn by doing extra work.
API endpoints in the same project
A +server.ts file exports functions named for HTTP verbs and returns standard Response objects:
// src/routes/api/webhook/+server.ts
import { json } from '@sveltejs/kit';
export async function POST({ request }) {
const payload = await request.json();
await handle(payload);
return json({ received: true });
}
Your webhooks, upload handlers, and public JSON live beside your pages, share your types, and deploy in the same step. For a lot of projects this removes the question of whether you need a separate backend service at all.
Adapters and deployment
One line in svelte.config.js decides where the built app runs:
import adapter from '@sveltejs/adapter-vercel';
export default { kit: { adapter: adapter() } };
Swap that import for adapter-node, adapter-cloudflare, adapter-netlify, or adapter-static and the same codebase targets a different platform. On Vercel there is no adapter configuration to think about at all; SvelteKit detects the platform and the zero-config path just works, including serverless functions for your server routes and edge rendering if you ask for it. Moving a project between hosts has never cost me more than a few minutes.
Why "small" is the feature
Here is the part I actually want to argue.
Every framework asks you to keep a number of concepts in working memory. Which of several data-fetching methods applies on this route. Whether this component runs on the server, the client, or both. When an effect re-runs and what it captured when it did. How the cache behaves and how long it holds. None of these are hard in isolation. The cost is that there are so many of them, they interact with each other, and the failure mode is code that compiles, renders, passes review, ships, and then breaks in a way nobody understands three weeks later.
SvelteKit keeps that list short on purpose. State is a variable. Derived state is $derived. A side effect is $effect. Data comes from load. Mutations go through form actions. A route is a folder. That is close to the entire working vocabulary, and it has stayed roughly that size across major versions instead of being redesigned every eighteen months.
For solo work and small teams that is worth more than any individual feature. I move between client codebases constantly and hand most of them off when they are finished. A stack a new developer can understand in an afternoon, and that I can drop back into a year later without relearning, is the actual deliverable. The fast builds and the small bundles are a bonus. The thing I am really buying is not having to think very hard about the framework so I can think hard about the work.
How it compares
Next.js. The larger ecosystem and the safer hire if you are staffing a React team next quarter. But Next has rebuilt its data and rendering model more than once (pages router, then the app router, then Server Components and their caching rules) and there is real, ongoing complexity in knowing which pattern applies where. SvelteKit's model has stayed small and stable. If your project is content-heavy or a straightforward product, that stability is worth a lot. If you need the absolute deepest bench of libraries, examples, and hiring pool, Next still wins that count.
Nuxt. The closest philosophical match. Filesystem routing, hybrid rendering, hybrid data loading, a strong batteries-included story, all built on Vue instead of Svelte. If your team already writes Vue, Nuxt is the obvious pick and you will feel at home. The Svelte side wins on runtime size and, to my taste, on how little ceremony a single component needs.
Remix / React Router. Remix pioneered much of what SvelteKit does well: nested routes, loaders, form actions, leaning hard on web platform standards instead of inventing around them. The ideas overlap heavily and the two frameworks often feel like cousins. The differences come down to the component model underneath and which ecosystem you want to live in.
Astro. Different job. Astro is superb for mostly-static content sites with islands of interactivity. If your site is 90 percent content and 10 percent app, look at Astro first. If that ratio is closer to even, or tips toward app, SvelteKit is the better fit.
Team context still decides a lot of this. If everyone around you writes React and there is no appetite to switch, Next.js is the pragmatic call and I would not argue you out of it.
When to reach for SvelteKit
Greenfield projects where you choose the stack. No migration cost, no legacy React to interoperate with. This is where it is strongest and where most of my work lands.
Content and marketing sites. Prerendering, a tiny runtime, and a headless CMS (I pair it with Sanity) get you fast, well-ranked pages with very little glue code.
Dashboards, portals, and internal tools. Server load functions and form actions cover most of what these need without a data-fetching library or a forms library in sight.
Where I would hesitate. A large existing React codebase. A team with deep React expertise and real deadline pressure. A project that leans on a specific library that only exists in the React world. The framework is production-ready; the constraint in these cases is people and ecosystem, not the technology.
Getting started
npx sv create my-app
cd my-app
npm run dev
The CLI walks you through TypeScript, linting, formatting, and optional add-ons like Tailwind, then leaves you with a dev server running on localhost:5173. You need Node 18 or newer and a working knowledge of HTML, CSS, and JavaScript. If you have used any component framework before, you will have a real page built the same afternoon, because there is very little standing between you and that.
From there the path is roughly: build a +layout.svelte for your shell, add routes as folders, move shared logic into src/lib, put server-only code in src/lib/server, and wire in a CMS or database through server load functions. That is the shape of nearly every project I have shipped.
A year later
The reason I keep coming back is not on any feature list. It is that a SvelteKit project ages well. The one I opened this piece with, the one I had not touched the docs for, is a client site I built early last year. I went back to it recently to add a section. The folder structure told me where everything was. The load function told me where the data came from. The form action told me how submissions were handled. Nothing had been reorganised out from under me by a framework upgrade.
That is not mastery on my part. It is just a small framework doing its job, and staying small while it does it.

