955: SvelteKit has solved data loading

Summary of 955: SvelteKit has solved data loading

by Wes Bos & Scott Tolinski - Full Stack JavaScript Web Developers

36mNovember 17, 2025

Overview of Syntax — Episode 955: "SvelteKit has solved data loading"

Hosts Wes Bos and Scott Tolinski deep-dive into SvelteKit Remote Functions (RPC-style APIs built into SvelteKit). They explain how remote functions change data loading, validation, forms, and client/server type-safety — and why this approach feels like a complete, end-to-end solution compared to other RPC libraries and the usual load/action patterns.

What Remote Functions are (high level)

  • Remote functions let you export server-backed functions from files named *.remote.ts / *.remote.js.
  • From a client-side Svelte component you import and call them like regular functions — SvelteKit handles network/SSR routing under the hood.
  • They unify server/client code patterns and give you:
    • top-level awaits in components (async Svelte)
    • built-in validation + consistent type flow
    • progressive enhancement for forms (works with or without JS)

How to enable and where to put them

  • Experimental flags required (at the time of recording): enable remoteFunctions and async in svelte.config.
  • Remote files: use .remote.ts/.remote.js and they can live anywhere in your project.
  • Import from client components just like local functions; SvelteKit determines if it runs server-side (SSR) or proxies over the network.

Core remote function types and features

  • query: read-only data fetch. Can be awaited (server-render) or used with query.loading / query.error / query.current.
  • query.batch: batches multiple queries from the same microtask (useful inside loops to avoid N requests — can collapse to one DB query).
  • form (mutation tied to HTML form): progressive-enhanced HTML form, validated by server schema, can render client field attributes and do per-field validation/errors.
  • command: programmatic mutation (non-form) — use when you don't want an HTML form.
  • pre-render: a query that only runs at build time (for static sites).
  • Other helpful APIs: .refresh (re-run the query), .set (inject result into a query to avoid extra DB call), optimistic UI support.

Forms: the standout feature

  • Form remote functions map schema => HTML inputs automatically. Example benefits:
    • Spread fields into inputs (e.g., createPost.fields.title) and SvelteKit outputs type, id, validation attributes, etc.
    • Works without JS (progressive enhancement) but also provides a client-side API (.enhance) for custom behaviors (submit hooks, toasts).
    • Programmatic control: mutation.fields.title.set, fields.value access, form.reset, submit, etc.
    • Validation and error messages are available without writing controlled inputs or manual state handling.
  • This provides a minimal-boilerplate, end-to-end form solution (schema → HTML → server validation → client UX).

Validation & Type-Safety (Standard Schema)

  • Remote functions lean on Standard Schema: write schemas in Zod / Valibot / Archetype / etc., convert to standard schema, and SvelteKit uses it for validation and type inference across client and server.
  • Result: seamless types without manually importing types between layers; strong TypeScript flow.
  • Standard schema adoption also helps interoperability with other tools, including AI-related tooling.

Async Svelte and UX advantages vs React

  • You can await values anywhere in Svelte components (top-level awaits, each-await loops) — this simplifies data fetching inside components.
  • This eliminates a lot of client-side useEffect/useState boilerplate needed in React (client-side React lacks async components; server-side React has them).
  • Combined with batching and query APIs, it's straightforward to avoid waterfalls and control SSR vs client fetching behavior.

Caching considerations and practical solutions

  • Remote functions themselves do not automatically provide component-level caching or cache headers on every call.
  • SvelteKit supports caching in some contexts (e.g., pre-render + Cache API), but component-level cache (like Next.js) is not yet fully present.
  • Two pragmatic approaches discussed:
    • Server-side: cache query results in a server key-value store before responding.
    • Client-side: Scott implemented a service-worker + IndexedDB stale-while-revalidate cache for remote function queries (intercepts queries, stores results in IndexedDB, serves cached copy on subsequent client loads). This improves perceived load and reduces network calls — simple, ~150 lines.
  • Cache strategy is complex and use-case dependent (page vs component cache, SWR semantics, revalidation).

Practical notes & migration experience

  • Scott rewrote the Syntax site data stack: switched to Drizzle + Postgres and migrated data loading to remote functions — resulting in less code, better validation, and strong TypeScript flow.
  • Remote functions feel “tip-to-tail” because SvelteKit controls the whole stack; for adding RPC to an existing framework, third-party tools still make sense, but integrated support is very compelling.

Actionable recommendations / todo list

  • To try remote functions:
    1. Turn on remoteFunctions and async in svelte.config (experimental).
    2. Create a file myFeature.remote.ts and export query/form/command functions.
    3. Import and call them from Svelte components like normal functions; await where you need SSR.
    4. Use Standard Schema (Zod/Valibot/etc.) for input validation and automatic typing.
    5. Use query.batch when querying in loops to avoid N requests.
    6. Use form remote functions to get progressive enhancement, auto-inputs, per-field errors, .enhance for JS hooks, and .set for cache updates or optimistic UI.
    7. Consider caching: server KV for shared cache, or a client service worker + IndexedDB for local SWR-like behavior.
  • If you rely on component-level cache semantics, monitor SvelteKit releases for richer cache APIs or emulate via server/client caches until native support matures.

Notable quotes / takeaways

  • “This is the solution for RPC, for data loading, for anything.” — Scott (captures the enthusiasm for remote functions)
  • “Form function is the goat.” — on the power of schema-driven, progressively enhanced forms
  • Core idea: remote functions + async Svelte = much less plumbing, stronger types, and cleaner separation of server/client responsibilities.

Verdict

Remote functions in SvelteKit (paired with async Svelte and Standard Schema) deliver a concise, type-safe, progressive and pragmatic approach to data loading and mutations. They remove many traditional pain points (route-level load functions, controlled form boilerplate, client/server type sync) and make it easy to build robust UX quickly — while still letting you extend with .enhance, batching, optimistic updates, and your own caching strategy. If you’re building with SvelteKit, they’re worth adopting and experimenting with now.


Hosts also close with quick “sick picks” (Bos: Bosch 800 dishwasher; Tolinski: cold-brew setup with double filtering + cotton sock) — small personal picks unrelated to the technical content.