--- title: Breaking changes --- While Svelte 5 is a complete rewrite, we have done our best to ensure that most codebases can upgrade with a minimum of hassle. That said, there are a few small breaking changes which may require action on your part. They are listed here. ## Components are no longer classes In Svelte 3 and 4, components are classes. In Svelte 5 they are functions and should be instantiated differently. If you need to manually instantiate components, you should use `mount` or `hydrate` (imported from `svelte`) instead. If you see this error using SvelteKit, try updating to the latest version of SvelteKit first, which adds support for Svelte 5. If you're using Svelte without SvelteKit, you'll likely have a `main.js` file (or similar) which you need to adjust: ```diff + import { mount } from 'svelte'; import App from './App.svelte' - const app = new App({ target: document.getElementById("app") }); + const app = mount(App, { target: document.getElementById("app") }); export default app; ``` `mount` and `hydrate` have the exact same API. The difference is that `hydrate` will pick up the Svelte's server-rendered HTML inside its target and hydrate it. Both return an object with the exports of the component and potentially property accessors (if compiled with `accesors: true`). They do not come with the `$on`, `$set` and `$destroy` methods you may know from the class component API. These are its replacements: For `$on`, instead of listening to events, pass them via the `events` property on the options argument. ```diff + import { mount } from 'svelte'; import App from './App.svelte' - const app = new App({ target: document.getElementById("app") }); - app.$on('event', callback); + const app = mount(App, { target: document.getElementById("app"), events: { event: callback } }); ``` > Note that using `events` is discouraged — instead, [use callbacks](https://svelte-5-preview.vercel.app/docs/event-handlers) For `$set`, use `$state` instead to create a reactive property object and manipulate it. If you're doing this inside a `.js` or `.ts` file, adjust the ending to include `.svelte`, i.e. `.svelte.js` or `.svelte.ts`. ```diff + import { mount } from 'svelte'; import App from './App.svelte' - const app = new App({ target: document.getElementById("app"), props: { foo: 'bar' } }); - app.$set('event', { foo: 'baz' }); + const props = $state({ foo: 'bar' }); + const app = mount(App, { target: document.getElementById("app"), props }); + props.foo = 'baz'; ``` For `$destroy`, use `unmount` instead. ```diff + import { mount, unmount } from 'svelte'; import App from './App.svelte' - const app = new App({ target: document.getElementById("app"), props: { foo: 'bar' } }); - app.$destroy(); + const app = mount(App, { target: document.getElementById("app") }); + unmount(app); ``` As a stop-gap-solution, you can also use `createClassComponent` or `asClassComponent` (imported from `svelte/legacy`) instead to keep the same API known from Svelte 4 after instantiating. ```diff + import { createClassComponent } from 'svelte/legacy'; import App from './App.svelte' - const app = new App({ target: document.getElementById("app") }); + const app = createClassComponent({ component: App, target: document.getElementById("app") }); export default app; ``` If this component is not under your control, you can use the `legacy.componentApi` compiler option for auto-applied backwards compatibility (note that this adds a bit of overhead to each component). This will also add `$set` and `$on` methods for all component instances you get through `bind:this`. ### Server API changes Similarly, components no longer have a `render` method when compiled for server side rendering. Instead, pass the function to `render` from `svelte/server`: ```diff + import { render } from 'svelte/server'; import App from './App.svelte'; - const { html, head } = App.render({ message: 'hello' }); + const { html, head } = render(App, { props: { message: 'hello' } }); ``` `render` also no longer returns CSS; it should be served separately from a CSS file. ### bind:this changes Because components are no longer classes, using `bind:this` no longer returns a class instance with `$set`, `$on` and `$destroy` methods on it. It only returns the instance exports (`export function/const`) and, if you're using the `accessors` option, a getter/setter-pair for each property. ## Whitespace handling changed Previously, Svelte employed a very complicated algorithm to determine if whitespace should be kept or not. Svelte 5 simplifies this which makes it easier to reason about as a developer. The rules are: - Whitespace between nodes is collapsed to one whitespace - Whitespace at the start and end of a tag is removed completely - Certain exceptions apply such as keeping whitespace inside `pre` tags As before, you can disable whitespace trimming by setting the `preserveWhitespace` option in your compiler settings or on a per-component basis in ``. ## More recent browser required Svelte now use Mutation Observers instead of IFrames to measure dimensions for `bind:clientWidth/clientHeight/offsetWidth/offsetHeight`. It also no longer listens to the `change` event on range inputs. Lastly, the `legacy` option was removed (or rather, replaced with a different set of settings). ## Changes to compiler options - The `false`/`true` (already deprecated previously) and the `"none"` values were removed as valid values from the `css` option - The `legacy` option was repurposed - The `hydratable` option has been removed. Svelte components are always hydratable now - The `enableSourcemap` option has been removed. Source maps are always generated now, tooling can choose to ignore it - The `tag` option was removed. Use `` inside the component instead - The `loopGuardTimeout`, `format`, `sveltePath`, `errorMode` and `varsReport` options were removed ## The `children` prop is reserved Content inside component tags becomes a [snippet prop](/docs/snippets) called `children`. You cannot have a separate prop by that name. ## Breaking changes in runes mode Some breaking changes only apply once your component is in runes mode. ### Bindings to component exports don't show up in rest props In runes mode, bindings to component exports don't show up in rest props. For example, `rest` in `let { foo, bar, ...rest } = $props();` would not contain `baz` if `baz` was defined as `export const baz = ...;` inside the component. In Svelte 4 syntax, the equivalent to `rest` would be `$$restProps`, which contains these component exports. ### Bindings need to be explicitly defined using `$bindable()` In Svelte 4 syntax, every property (declared via `export let`) is bindable, meaning you can `bind:` to it. In runes mode, properties are not bindable by default: you need to denote bindable props with the [`$bindable`](/docs/runes#$bindable) rune. ## Other breaking changes ### Stricter `@const` assignment validation Assignments to destructured parts of a `@const` declaration are no longer allowed. It was an oversight that this was ever allowed. ### Stricter CSS `:global` selector validation Previously, a compound selector starting with a global modifier which has universal or type selectors (like `:global(span).foo`) was valid. In Svelte 5, this is a validation error instead. The reason is that in this selector the resulting CSS would be equivalent to one without `:global` - in other words, `:global` is ignored in this case. ### CSS hash position no longer deterministic Previously Svelte would always insert the CSS hash last. This is no longer guaranteed in Svelte 5. This is only breaking if you [have very weird css selectors](https://stackoverflow.com/questions/15670631/does-the-order-of-classes-listed-on-an-item-affect-the-css). ### Scoped CSS uses :where(...) To avoid issues caused by unpredictable specificity changes, scoped CSS selectors now use `:where(.svelte-xyz123)` selector modifiers alongside `.svelte-xyz123` (where `xyz123` is, as previously, a hash of the `