← Back to Index
October 5, 2026•Web Development

SvelteKit 3 Is Here: Inside the Controversial Death of svelte.config.js and $lib

The Full-Stack Svelte 5 Era Begins

When Svelte 5 launched, it triggered one of the biggest architectural debates in frontend history by introducing Runes ($state, $derived, $effect) and dismantling its legacy compiler magic. But for production developers building full-stack web applications, Svelte 5 was only half the equation. The entire application lifecycle—routing, server-side data loading, form actions, asset optimization, and adapter deployments—lives inside SvelteKit.

In early October 2026, Rich Harris and the Svelte core team officially delivered the second half of the revolution: SvelteKit 3 is officially here.

As the announcement blog post boldly titled "Cleaning Out the Junk Drawer" declared, SvelteKit 3 is not an incremental update. It is an aggressive, uncompromising housecleaning. The team audited every historical convention that originated during the early SvelteKit beta years and asked a difficult question: Is this proprietary magic actually necessary, or does it isolate Svelte developers from the broader web platform?

The result is a release packed with major breaking changes: svelte.config.js is officially deleted, the beloved $lib import alias is retired, and legacy Svelte stores have been replaced by native reactive Runes across the entire full-stack lifecycle.

While existing codebases face an active migration, SvelteKit 3 cements a crucial shift in modern web architecture: framework-specific abstractions are dead; universal Vite and TypeScript primitives have won.


1. The Death of svelte.config.js: Consolidating into Vite

For years, starting a new SvelteKit project meant maintaining two separate build configuration files: vite.config.js for bundler plugins, and svelte.config.js for preprocessors, adapters, and kit-specific routing settings.

In SvelteKit 3, svelte.config.js is completely gone.

The team recognized that maintaining dual configuration files was an unnecessary layer of cognitive overhead that constantly confused newcomers and complicated CI build pipelines. Everything has been consolidated directly into your standard vite.config.ts:

// Modern SvelteKit 3: Everything in vite.config.ts
import { sveltekit } from '@sveltejs/kit/vite';
import adapter from '@sveltejs/adapter-node';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    sveltekit({
      adapter: adapter(),
      prerender: {
        entries: ['*'],
      },
    }),
  ],
});

By making the SvelteKit plugin inside Vite the single source of truth, developers can configure server adapters, pre-rendering rules, and Vite bundler options in a single unified TypeScript configuration with instant IDE autocompletion.


2. Retiring the Sacred $lib Alias

Since the earliest days of SvelteKit, $lib was sacred. Writing import Button from '$lib/components/Button.svelte' was the universal way to reference shared code inside src/lib.

In SvelteKit 3, the built-in $lib alias has been officially retired.

Why kill an alias that every developer loved? Because $lib was proprietary framework magic that violated standard TypeScript tooling:

  • External tools, linters, Monorepos, and IDEs that didn't know about SvelteKit's compiler had to be taught how to resolve $lib.
  • In enterprise monorepos with multiple packages, $lib caused collision and resolution ambiguities.

In SvelteKit 3, project path aliases are configured where they always belonged: inside tsconfig.json.

// Standard TypeScript Path Aliasing in tsconfig.json
{
  \"compilerOptions\": {
    \"paths\": {
      \"$lib\": [\"./src/lib\"],
      \"$lib/*\": [\"./src/lib/*\"],
      \"@components/*\": [\"./src/components/*\"]
    }
  }
}

By retiring the hardcoded framework alias and letting developers define standard TypeScript path mappings, SvelteKit applications now behave like standard, unopinionated TypeScript projects that work seamlessly with any third-party toolchain.


3. The End of Legacy Stores: Enter $app/state

In SvelteKit 1 and 2, accessing runtime context—such as current URL parameters, navigating state, or page data—relied on Svelte stores imported from $app/stores:

<!-- Legacy SvelteKit 2: Subscribing to stores -->
<script>
  import { page } from '$app/stores';
  // $page was an auto-subscribing store
  $: currentPath = $page.url.pathname;
</script>

<h1>Current Path: {currentPath}</h1>

In Svelte 5, stores were superseded by fine-grained Runes. Subscribing to an observable store just to check if the route changed was clumsy and inefficient.

SvelteKit 3 replaces $app/stores with $app/state:

<!-- Modern SvelteKit 3: Native Reactive Runes -->
<script lang=\"ts\">
  import { page } from '$app/state';
  // page is a fine-grained reactive proxy object!
  // No $ prefix, no store subscription overhead
  const currentPath = $derived(page.url.pathname);
</script>

<h1>Current Path: {currentPath}</h1>
<p>User ID: {page.data.user?.id}</p>

page is now a universal reactive proxy. You don't write $page. You access page.url, page.params, and page.data as plain JavaScript properties. When route parameters change, only the specific DOM nodes reading those properties re-render, eliminating whole-component re-evaluation.


4. Cleaning Out the Junk Drawer: Breaking Changes to Know

Beyond configuration and state, SvelteKit 3 eliminates several legacy patterns to streamline developer ergonomics:

1. Unified Form Actions & Error Handling

Form action responses and validation errors are now strictly typed via standard TypeScript generics. Throwing an error(404, 'Not Found') in a server load function now outputs a standardized, serializable JSON payload that integrates directly with Svelte 5's error boundaries.

2. Streamlined Server Load Functions

Data streaming in +page.server.ts has been optimized. Unawaited promises passed in load functions now stream directly to the client via HTTP chunks using native web streams, cutting Time-to-First-Byte (TTFB) on slow database queries.

3. Deprecated Legacy Utility Exports

Dozens of legacy helper functions that existed to patch missing browser primitives have been removed. SvelteKit 3 expects a modern environment with native Request, Response, and FormData standards.


5. What SvelteKit 3 Teaches Us About Modern Frameworks

The evolution of SvelteKit carries a profound lesson for frontend architecture in 2026: the era of proprietary framework walled gardens is over.

For years, frameworks tried to invent their own syntax, their own configuration formats, and their own import aliases. But as the broader web platform matured—with Vite becoming the universal build tool, TypeScript becoming the universal typechecker, and Web Standards becoming baseline—frameworks that cling to proprietary magic become liabilities.

By removing svelte.config.js, retiring $lib, and adopting universal Runes, SvelteKit has transformed from an eccentric indie framework into an industrial-grade, standards-compliant full-stack engine.


Conclusion: Upgrading to SvelteKit 3

If you are starting a new project in October 2026, SvelteKit 3 is the cleanest, most responsive full-stack framework in the JavaScript ecosystem. For existing SvelteKit 2 applications, the automated migration tool (npx svelte-migrate@latest sveltekit-3) handles the majority of vite.config.ts consolidations and store-to-state refactors.

The junk drawer is clean. The magic is gone. What remains is pure, high-performance web engineering.

Build something exceptional.

Custom web design and development, no templates.

Start a Project
SvelteKit 3 Launch: Breaking Changes, Vite & Runes — ZIAFTRA