How I Cut 140KB of Unused JS From My SvelteKit Bundle
rollup-plugin-visualizer findings, dynamic imports for heavy libs, and removing hydration on pages that don't need it.
Tested on: SvelteKit 2.16, Vite 6 Before: 218 KB JS transferred. After: 74 KB. TBT: 310ms → 45ms.
Step 1 — see what's actually in the bundle
npm install -D rollup-plugin-visualizer
vite.config.ts:
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
sveltekit(),
visualizer({ open: true, filename: 'stats.html', gzipSize: true })
]
});
Run npm run build. Open stats.html. My top offenders:
highlight.js— 89 KB (used on one code-block page)date-fns— 34 KB (I only usedformat())- Svelte runtime on a static about page — 41 KB
Fix highlight.js — dynamic import
Before (bad):
import hljs from 'highlight.js';
hljs.highlightAll();
After:
<script lang="ts">
import { onMount } from 'svelte';
let { code, lang } = $props();
onMount(async () => {
const hljs = (await import('highlight.js/lib/core')).default;
const langMod = await import(`highlight.js/lib/languages/${lang}`);
hljs.registerLanguage(lang, langMod.default);
// highlight single block only
});
</script>
Fix date-fns — stop importing the barrel
// BAD — pulls 600+ functions
import { format } from 'date-fns';
// GOOD
import format from 'date-fns/format/index.js';
Kill hydration on static pages
src/routes/about/+page.ts:
export const prerender = true;
export const csr = false;
csr = false means zero client JS for that route. About page dropped from 41 KB to 0 KB JS.
Error I hit with csr: false
Cannot use `onMount` in a component rendered with csr: false
A shared <Header> had onMount for a mobile menu. Split it:
Header.svelte— static markup onlyMobileMenu.svelte— loaded with{#await import('./MobileMenu.svelte')}only on pages withcsr: true
Verify
npm run build
# Check .svelte-kit/output/client/_app/immutable/chunks/
ls -la build/_app/immutable/chunks/ | wc -l
Numbers
First Load JS (home): 218 KB → 74 KB
highlight.js chunk: lazy-loaded, not in initial bundle
date-fns: 34 KB → 2.1 KB
about page JS: 41 KB → 0 KB
Rule: if a page doesn't need interactivity, set csr: false. If a lib is heavy and rare, dynamic import it.