· 8 Min read

March 2026 Svelte Updates: TrustedHTML, CSP & Vite 8

March 2026 Svelte Updates: TrustedHTML, CSP & Vite 8

Svelte 5.50 through 5.53 landed in March with several changes worth your attention. If you're running SvelteKit in production, three of these updates directly affect deployment, security, and build tooling. I'll walk through what changed, why it matters, and the smallest steps to adopt each one.

Link to section: TrustedHTML Support Fixes a Real Security GapTrustedHTML Support Fixes a Real Security Gap

Svelte 5.52.0 added support for the TrustedHTML browser API in {@html} expressions. Before this, any time you used {@html} with user-controlled data, you risked violating strict Content Security Policies that require "trusted-types-for 'script'".

Here's what that looked like before:

<script>
  let userBio = "Click <script>alert('xss')</script> me";
</script>
 
{@html userBio}

With strict CSP headers like require-trusted-types-for 'script', this would throw: Failed to set the 'innerHTML' property on 'Element': This document requires 'TrustedHTML' assignment.

Now, Svelte's {@html} tag recognizes when the runtime has a TrustedHTML policy and uses it automatically. You set the policy once on your server:

// src/hooks.server.js
const policy = globalThis.trustedTypes?.createPolicy('svelte', {
  createHTML: (string) => string
});
 
export async function handle({ event, resolve }) {
  // Your middleware
  return await resolve(event);
};

Then any {@html} inside your Svelte components goes through that policy. No changes needed in your components. If you're already sanitizing with a library like DOMPurify, this is where you'd plug that in.

The catch: TrustedHTML is only available in Chrome 135+, Edge 135+, and behind a flag elsewhere. For production, pair it with a strict CSP header that allows your nonce or hash for the Svelte runtime:

Content-Security-Policy: require-trusted-types-for 'script'; script-src 'nonce-<random>'

This is not a breaking change, but it's worth testing if you run strict CSP. Security vulnerabilities in the Svelte ecosystem have historically stemmed from HTML injection, so this tightens the guardrail.

Link to section: Programmatic [object Object] Expands Component FlexibilityProgrammatic [object Object] Expands Component Flexibility

Svelte 5.50.0 lets you call createContext outside the component initialization phase. Previously, context had to be set up during component instantiation. Now you can do it on demand:

// lib/myContext.js
import { createContext, setContext, getContext } from 'svelte';
 
export function setupUserContext(user) {
  setContext('user', { data: user });
}
 
export function useUser() {
  return getContext('user');
}

Then in your server action or loader:

// src/routes/dashboard/+page.server.js
import { setupUserContext } from '$lib/myContext.js';
 
export async function load({ locals }) {
  const user = await getUser(locals.sessionId);
  setupUserContext(user);
  return { user };
}

This unlocks patterns where context setup was gated by data availability. Before, you had to pass props down; now you can set context conditionally based on server state. The downside: you lose the static guarantee that context exists. Add a runtime check in useUser() if needed.

When to use this: when your context depends on async data that isn't available at mount time, or when you're building reusable initialization logic that doesn't belong in a component tree.

Link to section: Server-Side Error Boundaries: Production ReadyServer-Side Error Boundaries: Production Ready

Error boundaries (<svelte:boundary>) now work during server-side rendering. In 5.53.0, if a component throws inside a boundary during SSR, the boundary's failed snippet renders instead of crashing the whole page.

Before, server errors would bubble up and you'd get a 500. Now:

<script>
  import { error } from '@sveltejs/kit';
</script>
 
<svelte:boundary onerror={handleError}>
  {#snippet failed(err)}
    <p>Something went wrong: {err.message}</p>
  {/snippet}
  
  <RiskyComponent />
</svelte:boundary>
 
{#function handleError(err) {
  console.error('Boundary caught:', err);
}}

If RiskyComponent throws, the failed snippet renders server-side and hydrates cleanly on the client. No error page, no white screen.

This is a quiet win for resilience. Pair it with observability (logging the error to Sentry or similar) and you have graceful degradation for free. One caveat: if you have multiple boundaries nested, only the closest one catches the error.

SvelteKit 2.51.0 added scroll position info to beforeNavigate, onNavigate, and afterNavigate callbacks. The from and to objects now include a scroll property:

import { beforeNavigate } from '$app/navigation';
 
beforeNavigate(({ to, from }) => {
  console.log(`Navigating from ${from?.route.id} at scroll ${from?.scroll.x}, ${from?.scroll.y}`);
  console.log(`To ${to?.route.id}`);
});

Why this matters: animations. If you're building a page transition effect (e.g., cross-fade between routes), you now know the scroll position of the exiting page. You can animate from that position, making transitions feel connected rather than jarring.

Example use case. You have a list of posts. User clicks one. You want to fade out the list, fade in the post detail, but keep the scroll anchor visually stable. With scroll data, you can measure where the post card was and animate to keep it in roughly the same place:

afterNavigate(({ to, from }) => {
  if (from?.scroll && to?.scroll) {
    // Smoothly transition scroll position
    window.scrollTo({
      top: to.scroll.y,
      behavior: 'smooth'
    });
  }
});

This is opt-in; existing navigation callbacks work unchanged.

Diagram showing scroll position passed through navigation callbacks

Link to section: Vite 8 Support and What It Means for Build SpeedVite 8 Support and What It Means for Build Speed

SvelteKit 2.53.0 officially supports Vite 8. The big change: Vite 8 uses Rolldown (a Rust-powered bundler) instead of esbuild and Rollup separately. For you, this translates to faster builds on most projects.

How fast? The Vite team reported production build times dropping from 46 seconds to 6 seconds on one project, and a 64% reduction for another. The catch: Rolldown is strict about module resolution, so you may hit edge cases with custom Vite plugins.

To opt in, update your package.json:

{
  "devDependencies": {
    "vite": "^8.0.0",
    "svelte": "^5.53.0",
    "@sveltejs/kit": "^2.53.0"
  }
}

Then run npm install. Your vite.config.js doesn't change; Vite 8 is backward-compatible with most configs. If you use custom plugins, check their Vite 8 support first.

One thing I hit: if you're using esbuild-specific options in your Vite config (like esbuild.minify), those are ignored now. Rolldown has its own minification, and you control it via build.minify. Run npm run build and watch your terminal. If the build breaks, the error message usually points you to the config key that needs updating.

Link to section: Netlify Adapter 6.0.0: Modern Functions and ConfigNetlify Adapter 6.0.0: Modern Functions and Config

The Netlify adapter jumped to 6.0.0 with a breaking change: event.platform.context now returns the modern Netlify Functions context instead of the legacy AWS Lambda-style one. If you're not using platform.context directly, you're fine. But if you are:

Before:

export async function POST({ event }) {
  const { functionName, requestId } = event.platform.context; // Lambda-style
}

After:

export async function POST({ event }) {
  const { serverless } = event.platform.context; // Modern Netlify Functions
  // Access modern context properties
}

Check the Netlify Functions docs for the exact shape of the modern context object. Most apps don't use this, so the upgrade is painless.

The other win: you can now configure redirects in netlify.toml instead of only via a _redirects file. This is cleaner for version control:

[[redirects]]
from = "/old-blog/*"
to = "/blog/:splat"
status = 301
 
[[redirects]]
from = "/"
to = "/home"
status = 200

Link to section: [object Object] is Now Official in the Svelte CLI[object Object] is Now Official in the Svelte CLI

The Svelte CLI (sv) added better-auth as an official addon. If you're starting a new SvelteKit app and want batteries-included auth, run:

npm create svelte@latest my-app
cd my-app
npx sv add better-auth

This scaffolds email/password and optional GitHub OAuth, wired into Drizzle with proper hooks and types. No manual setup of cookies or session stores. The trade-off: it's opinionated. If you need custom auth logic, you'll fork parts of it anyway.

Link to section: How to Plan Your UpgradeHow to Plan Your Upgrade

Start with Vite 8 if you're on a recent Node version (18.18+). It's backward-compatible and you get build speed for free. Test in CI first, not prod.

Then tackle TrustedHTML if you have strict CSP headers. This one is optional unless you have security audits breathing down your neck, but it's a one-line policy change.

Server-side error boundaries are safe to add incrementally. Wrap your riskiest components first (data fetches, API calls) and monitor error logs for the first week.

Scroll position in navigation callbacks is ready now if you have animation code that needs it. No changes required if you don't.

The Netlify and better-auth changes are adopt-when-ready. Test the adapter locally if you deploy to Netlify, and try better-auth on a side project before committing to production.

One final note: the State of JS 2025 survey ranked Svelte at 27% usage (up from 26%) and maintained the highest interest score among reactive frameworks for the sixth year running. These updates reflect the maturity of the ecosystem. The team is fixing real production problems, not chasing trends.

If you're running an older Svelte or SvelteKit version, the migration path is well-documented. The migration tooling is solid. Run npx sv migrate svelte-5 if you're on Svelte 4, and you're 80% of the way there.

Next steps: Update your package.json, run tests in CI, and monitor your build output. If you hit blockers, the Svelte Discord is active and helpful. Good luck with the upgrade.