Skip to main content
PORTFOLIO

SvelteKit in July 2026: Remote Functions, .live() Queries, and the Road to SvelteKit 3

Mohit Byadwal

SvelteKit in July 2026: Remote Functions, .live() Queries, and the Road to SvelteKit 3

Quick answer (AEO): SvelteKit’s June and July 2026 updates introduce remote function File uploads (no manual FormData), .live() queries for real-time server data, inline Vite config (skip svelte.config.js entirely), and the first explicit environment variable preview that will replace $env/* modules in SvelteKit 3. TypeScript 6.0 support landed in May.

The big picture: SvelteKit is preparing for version 3

The SvelteKit team isn’t just shipping incremental features—they’re laying the migration path to SvelteKit 3. The pattern is familiar from Svelte 4→5: introduce the new API as experimental, let it stabilize, then make it the default in the next major version.

Key SvelteKit 3 signals visible today:

  1. Explicit env vars will replace $env/static/private, $env/dynamic/public, etc.
  2. Remote functions are maturing toward a stable API surface.
  3. Inline Vite config reduces the framework-specific configuration surface.
  4. New {const} declaration tags in templates are expanding across tooling.

Remote functions: File uploads without FormData gymnastics

Remote functions (SvelteKit’s answer to server actions) now accept File objects directly as parameters. Previously, file uploads required wrapping in FormData and handling multipart encoding manually.

<script>
  import { uploadAvatar } from './api.server.ts';
  
  let file = $state<File | null>(null);
  
  async function handleUpload() {
    if (!file) return;
    // File object passes directly — no FormData wrapper
    const result = await uploadAvatar(file);
    console.log(result.url);
  }
</script>

<input type="file" accept="image/*" onchange={(e) => file = e.target.files?.[0] ?? null} />
<button onclick={handleUpload}>Upload</button>
// api.server.ts
export async function uploadAvatar(file: File) {
  const buffer = await file.arrayBuffer();
  const url = await storage.put(`avatars/${file.name}`, buffer);
  return { url };
}

Why it matters: This eliminates an entire class of boilerplate that every file-upload feature requires. The DX improvement is meaningful for apps with media handling, document upload, or avatar management.

Remote function commands

The distinction between queries (read data) and commands (mutate state) is now explicit in the API. Commands handle the File parameter support; queries support the new .live() method.

.live() queries: real-time data without WebSocket boilerplate

The most exciting addition is .live() — a method on query functions that returns a reactive value, automatically updating when the server-side data changes.

<script>
  import { getNotifications } from './data.server.ts';
  
  // .live() returns a reactive value that auto-updates
  let notifications = getNotifications.live();
</script>

{#each notifications.current as notification}
  <div class="notification">{notification.message}</div>
{/each}

Under the hood, .live() establishes a connection (likely SSE or WebSocket, transport-abstracted) and pushes updates to the client when the underlying data changes. The key design decision: you write a normal query function, and .live() makes it real-time without changing the server-side implementation.

Comparison with prior art:

  • tRPC subscriptions: Similar concept, but SvelteKit’s version is more tightly integrated with the routing and load system.
  • Supabase realtime: Database-level; .live() is application-level, so you can combine multiple data sources.
  • Socket.io patterns: Manual event handling; .live() is declarative and framework-managed.

When to use .live() vs. polling:

  • Use .live() for notifications, collaborative editing indicators, real-time dashboards.
  • Stick with polling (or normal load functions) for data that changes infrequently or where stale-while-revalidate is acceptable.

Inline Vite config: one fewer config file

Starting with SvelteKit 2.63, you can define your SvelteKit configuration directly inside vite.config.js and skip svelte.config.js entirely:

// vite.config.js — everything in one place
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    sveltekit({
      // SvelteKit config lives here now
      adapter: 'auto',
      prerender: { entries: ['*'] },
      alias: { '$components': './src/lib/components' }
    })
  ]
});

Why it matters: svelte.config.js was always slightly awkward—it used a different module format, required separate IDE support, and split configuration across two files for no strong reason. Inline config reduces cognitive overhead and works better with Vite’s plugin ecosystem.

Migration: This is optional. svelte.config.js continues to work. But new projects will likely start with the inline pattern.

Explicit environment variables (SvelteKit 3 preview)

The current $env/static/private, $env/static/public, $env/dynamic/private, $env/dynamic/public import paths are verbose and confusing to newcomers. The experimental replacement:

// Declare and type your env vars in one place
import { env } from '$env';

// Type-safe, with clear public/private boundary
const apiKey = env.private.API_KEY; // server only
const publicUrl = env.public.SITE_URL; // available on client

This is a preview of SvelteKit 3’s approach: fewer magic import paths, explicit declarations, and TypeScript-first ergonomics. The current $env/* modules will be deprecated when this stabilizes.

TypeScript 6.0 support

SvelteKit 2.56 (May 2026) added full TypeScript 6.0 compatibility:

  • verbatimModuleSyntax works correctly with Svelte component imports.
  • module: "nodenext" default aligns with SvelteKit’s Vite pipeline.
  • strict: true default catches more issues in +page.ts and +server.ts files.
  • $env modules generate proper type declarations under the new inference rules.

If you’re upgrading to TS 6.0, SvelteKit is ready. No adapter or plugin changes needed.

New {const} declaration tags

Templates now support {const} for declaring local variables within template blocks:

{#each items as item}
  {@const total = item.price * item.quantity}
  {@const formatted = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(total)}
  <div class="line-item">
    <span>{item.name}</span>
    <span>{formatted}</span>
  </div>
{/each}

This reduces the need to extract computation into separate $derived values or helper functions for template-local concerns.

Navigation improvements

Navigation callbacks (beforeNavigate, onNavigate, afterNavigate) now include scroll position information via the scroll property on from and to targets. This enables:

  • Scroll-position-aware transition animations.
  • Restoring scroll on browser back/forward navigation.
  • Conditional animation based on scroll direction.

What this means for production apps

The throughline across all these changes: SvelteKit is becoming more opinionated about the happy path while reducing configuration surface. The framework wants you to:

  1. Write server functions and let the framework handle the RPC layer.
  2. Use .live() for real-time instead of managing WebSocket connections.
  3. Configure once in vite.config.js instead of splitting across files.
  4. Declare env vars explicitly instead of memorizing import path conventions.

For teams already on SvelteKit, these are incremental upgrades. For teams evaluating frameworks, SvelteKit’s mid-2026 story is compelling: Svelte 5 runes for reactivity, remote functions for server communication, .live() for real-time, and a clear path to SvelteKit 3 without breaking changes.


Related reading: Mastering Svelte 5 runes, TypeScript 6.0 migration, and component composition for design systems.