Svelte 5 is alive
Our biggest release yet
After almost 18 months of development, comprising thousands of commits from dozens of contributors, Svelte 5 is finally stable.
It’s the most significant release in the project’s history. Svelte 5 is a ground-up rewrite: your apps will be faster, smaller and more reliable. You’ll be able to write more consistent and idiomatic code. For newcomers to the framework, there’s less stuff to learn.
Despite all that, Svelte is almost completely backwards-compatible with Svelte 4 — for the majority of users, the initial upgrade will be completely seamless:
{
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"svelte": "^4",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"svelte": "^5",
// …
}
}
What is Svelte?
Svelte is a framework for building user interfaces on the web. It uses a compiler to convert declarative component code, based on HTML, CSS and JavaScript, into tightly optimised JavaScript.
Because the compiler shifts a lot of the work out of the browser and into npm run build
, Svelte apps are small and fast. But beyond that, Svelte is designed to be an enjoyable and intuitive way to build apps: it prioritises getting stuff done.
The team behind Svelte also maintains SvelteKit, an application framework that handles routing and data loading and server-side rendering and all the gory details that go into building modern websites and apps.
What changed, and why?
For one thing, we’ve overhauled our website. You can read more about that here.
As for Svelte itself, we’ll cover the why first. We’re not fans of change for change’s sake — in fact, Svelte changed less than any other major framework between 2019 (when we launched Svelte 3) and now, which is an eon in front end development. And people really liked Svelte 3 and 4 — it routinely tops developer surveys of satisfaction.
So when we make changes, we don’t make them lightly.
With more and more people building more and bigger applications with Svelte, the limitations of some of our original design decisions started to become more apparent. For example, in Svelte 4, reactivity is driven entirely by the compiler. If you change a single property of a reactive object in Svelte 4, the entire object is invalidated, because that’s all the compiler can realistically do. Meanwhile, other frameworks have adopted fine-grained reactivity based on signals, leapfrogging Svelte’s performance.
Equally, component composition is more awkward in Svelte 4 than it should be, largely because it treats event handlers and ‘slotted content’ as separate concepts, distinct from the props that are passed to components. This is because in 2019 it seemed likely that web components would become the primary distribution mechanism for components, and we wanted to align with the platform. This was a mistake.
And while the $:
construct for reactively re-running statements is a neat trick, it turned out to be a footgun. It conflated two concepts (derived state and side-effects) that really should be kept separate, and because dependencies are determined when the statement is compiled (rather than when it runs), it resists refactoring and becomes a magnet for complexity.
Svelte 5 removes these inconsistencies and footguns. It introduces runes, an explicit mechanism for (among other things) declaring reactive state:
let count = 0;
let let count: number
count = function $state<0>(initial: 0): 0 (+1 overload)
namespace $state
$state(0);Interacting with state is unchanged: with Svelte — unlike other frameworks — count
is just a number, rather than a function, or an object with a value
property, or something that can only be changed with a corresponding setCount
:
function function increment(): void
increment() {
let count: number
count += 1;
var console: Console
The console
module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
- A
Console
class with methods such as console.log()
, console.error()
and console.warn()
that can be used to write to any Node.js stream.
- A global
console
instance configured to write to process.stdout
and
process.stderr
. The global console
can be used without calling require('console')
.
Warning: The global console object’s methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O
for
more information.
Example using the global console
:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(new Error('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console
class:
const out = getStreamSomehow();
const err = getStreamSomehow();
const myConsole = new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(new Error('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';
myConsole.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to err
console.Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)
Prints to stdout
with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()
).
const count = 5;
console.log('count: %d', count);
// Prints: count: 5, to stdout
console.log('count:', count);
// Prints: count: 5, to stdout
See util.format()
for more information.
log({ count: number
count });
}
Runes can be used in .svelte.js
and .svelte.ts
modules in addition to .svelte
components, meaning you can create reusable reactive logic using a single mechanism.
Event handlers are now just props like any other, making it easy to (for example) know whether the user of your component supplied a particular event handler (which can be useful for avoiding expensive setup work), or to spread arbitrary event handlers onto some element — things that are particularly important for library authors.
And the slot
mechanism for passing content between components (together with the confusing let:
and <svelte:fragment>
syntax) has been replaced with {#snippet …}
, a much more powerful tool.
Beyond these changes are countless improvements: native TypeScript support (no more preprocessors!), many bugfixes, and performance and scalability improvements across the board.
How do I upgrade?
If you’re currently on Svelte 3, begin by migrating to Svelte 4.
From there, you can update your package.json
to use the newest version of svelte
and ancillary dependencies like vite-plugin-svelte
.
You don’t have to update your components immediately — in almost all cases, your app will continue working as-is (except faster). But we recommend that you begin to migrate your components to use the new syntax and features. You can migrate your entire app with npx sv migrate svelte-5
, or — if you’re using VS Code with the Svelte extension — you can migrate components one at a time by selecting ‘Migrate Component to Svelte 5 Syntax’ in your command palette.
Svelte has a large and robust ecosystem of component libraries that you can use in your applications such as shadcn-svelte, Skeleton, and Flowbite Svelte. But you don’t have to wait for these libraries to upgrade to Svelte 5 in order to upgrade your own application.
Eventually, support for Svelte 4 syntax will be phased out, but this won’t happen for a while and you’ll have plenty of warning.
For more details, see the comprehensive Svelte 5 migration guide.
Our new CLI
Along with a new version of Svelte, we have a new Command Line Interface (CLI), sv
, to go with it. You can learn all about it in the announcement blog post.
What’s next?
We plan to release a new version of SvelteKit in the near future that takes advantage of the new Svelte 5 features. In the meantime, you can use Svelte 5 with SvelteKit today, and npx sv create
will create a new SvelteKit project with Svelte 5 installed alongside it.
After that, we have a laundry list of ideas we want to implement in Svelte itself. This release is the foundation for many improvements that would have been impossible to build on top of Svelte 4, and we can’t wait to roll up our sleeves.