Preservation of local component state after JS updates is disabled to avoid unpredictable and error-prone behavior. Since svelte-5, hmr is integrated in compileOptions, but the sentiment in the previously used svelte-hmr docs still applies.
Please note that if you only edit the <style>
node, a separate CSS update can be applied where component state is 100% preserved.
The <style>
node should be last to ensure optimal HMR results. This is also the default order with prettier-plugin-svelte.
Good:
<script></script>
<div></div>
<style></style>
Bad:
<script></script>
<style></style>
<!-- this template element is below the style node and may cause extra HMR updates -->
<div></div>
You have to use the lang="ts"
attribute for Vite to parse it. Never lang="typescript"
or type="text/typescript"
.
Good:
<script lang="ts"></script>
Bad:
<!-- These are not detected by Vite -->
<script lang="typescript"></script>
<script type="text/typescript"></script>
Global styles should always be placed in their own stylesheet files whenever possible, and not in a Svelte component's <style>
tag. The stylesheet files can then be imported directly in JS and take advantage of Vite's own style processing. It would also significantly improve the dev server startup time.
Good:
/* global.scss */
html {
color: $text-color;
}
// main.js
import './global.scss';
Bad:
<style lang="scss">
:global(html) {
color: $text-color;
}
</style>
cssHash
is fixed in development for CSS HMR in Svelte components, ensuring that the hash value is stable based on the file name so that styles are only updated when changed.
However, cssHash
is respected in production builds as HMR is a dev-only feature.
If you are building a Vite plugin that transforms CSS or JS, you can add a api.sveltePreprocess: PreprocessorGroup
to your Vite plugin definition and it will be added to the list of Svelte preprocessors used at runtime.
const vitePluginCoolCss = {
name: 'vite-plugin-coolcss',
api: {
sveltePreprocess: {
/* your PreprocessorGroup here */
}
}
/*... your cool css plugin implementation here .. */
};
Prebundling dependencies is an optimization in Vite.
We only use prebundling during development, the following does not apply to or describe the built application
It is required for CJS dependencies, as Vite's development server only works with ES modules on the client side. Importantly for Svelte libraries and ES modules, it also reduces the number of http requests when you load a page from the dev server and caches files so subsequent starts are even faster.
The way prebundling Svelte libraries affects your dev-server load times depends on the import style you use, index or deep:
Offers better DX but can cause noticable delays on your machine, especially for libraries with many files.
import { SomeComponent } from 'some-library'
+ only one request per library
+ intellisense for the whole library after first import
- compiles the whole library even if you only use a few components
- slower build and dev-server ssr
Offers snappier dev and faster builds for libraries with many files at the expense of some DX
import SomeComponent from 'some-library/src/SomeComponent.svelte'
+ compiles only the components you import
+ faster build and dev-server ssr
- one request per import can slow down initial load if you use a lot of components
- intellisense only for imported components
Do not use it in combination with prebundling!
Prebundling works by reading your .svelte
files from disk and scanning them for imports. It cannot detect
added/changed/removed imports and these then cause extra requests, delays and render the prebundled files from the initial scan moot.
If you prefer to use these tools, please exclude the libraries you use them with from prebundling.
If you want to disable prebundling for a single library, use optimizeDeps.exclude
// vite.config.js
export default defineConfig({
optimizeDeps: {
exclude: ['some-library'] // do not pre-bundle some-library
}
});
Or disable it for all Svelte libraries
// svelte.config.js
export default {
vitePlugin: {
prebundleSvelteLibraries: false
}
};
There is no golden rule, but you can follow these recommendations:
- Never combine plugins or preprocessors that rewrite imports with prebundling
- Start with index imports and if your dev-server or build process feels slow, run the process with
DEBUG="vite-plugin-svelte:stats"
to check compile stats to see if switching to deep imports can improve the experience. - Do not mix deep and index imports for the same library, use one style consistently.
- Use different import styles for different libraries where it helps. E.g. deep imports for the few icons of that one huge icon library, but index import for the component library that is heavily used.
This warning only occurs if you use non-default settings in your vite config that can cause problems in combination with prebundleSvelteLibraries. You should not use prebundleSvelteLibraries during build or for ssr, disable one of the incompatible options to make that warning (and subsequent errors) go away.
You really shouldn't. Svelte and Vite are esm first and the ecosystem is moving away from commonjs and so should you. Consider migrating to esm.
In case you have to, use dynamic import to load vite-plugin-svelte's esm code from cjs like this:
// vite.config.cjs
const { defineConfig } = require('vite');
- const { svelte } = require('@sveltejs/vite-plugin-svelte');
module.exports = defineConfig(async ({ command, mode }) => {
+ const { svelte } = await import('@sveltejs/vite-plugin-svelte');
return {plugins:[svelte()]}
}
And for vitePreprocess
you have to set up a lazy promise as top-level-await doesn't work for esm imports in cjs:
- const {vitePreprocess} = require('@sveltejs/vite-plugin-svelte')
+ const vitePreprocess = import('@sveltejs/vite-plugin-svelte').then(m => m.vitePreprocess())
module.exports = {
- preprocess: vitePreprocess()
+ preprocess: {
+ script:async (options) => (await vitePreprocess).script(options),
+ style:async (options) => (await vitePreprocess).style(options),
+ }
}
If you see a warning logged for this when using a Svelte library, please tell the library maintainers.
Using the svelte
field in package.json
to point at .svelte
source files is deprecated and you must use a svelte
export condition.
vite-plugin-svelte 3 still resolves it as a fallback, but in a future major release this is going to be removed and without exports condition resolving the library is going to fail.
Example:
// package.json
"files": ["dist"],
"svelte": "dist/index.js",
+ "exports": {
+ ".": {
+ "svelte": "./dist/index.js"
+ }
}
You can also add individual exports of .svelte files in the exports map which gives users a choice to also use deep imports. See the faq about vite and prebundling why they can be useful at times.
Library authors are highly encouraged to update their packages to add the new exports condition as outlined above. Check out svelte-package which already supports it.
For backwards compatibility, you can keep the
svelte
field in addition to theexports
condition. But make sure that both always resolve to the same files.
This is not supported out of the box. To resolve assets, you have to either import them like this:
<script>
import assetUrl from './asset.png';
</script>
<img src="{assetUrl}" />
or use a separate tool to add this functionality. The 2 recommended solutions are sveltejs/enhanced-img (only for image elements) and svelte-preprocess-import-assets (for all asset URLs).