I have upgraded svelte-lexical
to Svete 5. It was a relatively smooth process considering the significant change of syntax.
Here is the story of the upgrade:
1- Run the migration utility
The Svelte team has created a migration utility to upgrade to Svelte 5. It must be executed on each project separately (it doesn’t work across monorepo). Let’s start by migrating all the projects.
pnpm dlx sv migrate svelte-5
2- Install packages
Install packages after the migration utility has made the necessary changes:
pnpm i
3- Launch the project
pn dev
I encountered an error regarding enum
not being supported.
[plugin:vite-plugin-svelte] src/lib/components/toolbar/FontSizeEntry.svelte:15:2 TypeScript language features like enums are not natively supported, and their use is generally discouraged. Outside of `<script>` tags, these features are not supported. For use within `<script>` tags, you will need to use a preprocessor to convert it to JavaScript before it gets passed to the Svelte compiler. If you are using `vitePreprocess`, make sure to specifically enable preprocessing script tags (`vitePreprocess({ script: true })`)
https://svelte.dev/e/typescript_invalid_feature
The error message provided clear instructions on how to fix this one. I enabled script tags preprocessing by vite in svelte.config.js
.
4- New Svelte Component Type
Next, I encountered an error about the SvelteComponent
type not found. The migration guide (https://svelte.dev/docs/svelte/v5-migration-guide) indicates that SvelteComponent
is deprecated and Component
should be used instead.
I replaced all references to SvelteComponent
type with Component
.
The app now launches without any crashes, but not all functionality works yet, and numerous linting errors exist.
5- The latest Svelte package
The migration tool upgraded the svelte version to 5.0.0
. It makes sense to fetch the latest version of Svelte and related packages so that I get all the fixes.
The following packages are updated:
"svelte": "^5.10.1",
"svelte-check": "^4.1.1",
"@sveltejs/adapter-auto": "^3.3.1",
"@sveltejs/kit": "^2.10.1",
"@sveltejs/package": "^2.3.7",
"@sveltejs/vite-plugin-svelte": "^4.0.2",
6- Upgrade ESLint
ESLint is complaining about runes ($state, $props). Let’s upgrade ESLint to the latest version. It resolved the issue, took a bit of effort though. This is because the ESLint config file format has changed in version 9 and I have to convert my config to the new format.
The new flat config file is called eslint.config.js
. It combines both .eslintrc.cjs
and .eslintignore
.
The following packages are upgraded:
"eslint": "^9.16.0",
"typescript-eslint": "^8.18.1",
"eslint-plugin-import": "^2.31.0",
"@eslint/compat": "^1.2.4",
"@eslint/eslintrc": "^3.2.0",
"@eslint/js": "^9.17.0",
"globals": "^15.14.0",
And the following should be removed:
"@typescript-eslint/eslint-plugin": "^8.18.0",
"@typescript-eslint/parser": "^8.18.0",
7- Svelte Check
I have run Svelte Check and getting 62 errors and 4 warnings.
Let’s resolve them one by one.
8- Components are no longer classes
One of the issues is components cannot be created with new
keyword as they are not classes anymore.
The migration guide explains it well here (https://svelte.dev/docs/svelte/v5-migration-guide#Components-are-no-longer-classes).
I have used createClassComponent
to have compatibility with Svelte 4.
9- Using the new Component
API
For a text editor, creating components on the fly is part of the core functionality. I don’t want to keep using the legacy createClassComponent
API in this part of the library. Let’s try replacing it with the new mount API.
It is easy to adopt the new API for component creation as documented here. But I am also using component.$set
to update the properties of the components that is not available in Svelte 5. I have refactored the code to use the Runes approach. Essentially, it involves updating the properties that were originally passed to the component (See migration guide for details).
Successfully replaced the legacy createClassComponent
with the new mount and runes API.
10- Event Bubbler
The event bubbler code injected by the migration utility is not working for me. Getting rid of the legacy event bubbler introduced, instead using onclick
and other events as Component properties. This streamlines code and improves readability.
After the last step, the app seems to be fully functional without any errors. Yay!
11- Unit Tests
Unit tests are not working. It is because my Vitest version doesn’t understand the new Svelte Component
type. Upgrading Vitest to the latest version resolves the issue.
Following packages are upgraded:
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/svelte": "^5.2.6",
"@vitest/ui": "^2.1.8",
"jsdom": "^25.0.1",
"vitest": "^2.1.8"
12- Use Runes Mode
I could see some of the components are in legacy mode, they don’t need to be.
We can force them into Runes mode by inserting the following directive at the top of the file.
<svelte:options runes={true} />
13- TS Type Errors
Fix all TypeScript type definition errors introduced during migration to Svelte 5. These are easy to fix.
14- Accessibility Warnings
Fix the new accessibility warnings. In my case, these are mostly about defining aria-label
on buttons and removing ignore comments
that are not applicable anymore.
15- Code Formatting
Fix formatting issues introduced during migration. Pretty straightforward.
16- Run Tests
Rest of the errors were pretty minor ones and easy to fix. With all svelte-check, ESLint and Prettier errors fixed, let’s execute the tests.
All 284 tests are passing 🙂
Kudos to the Svelte team for making such a major migration so easy. This must have required a lot of hard work.