HiveBrain v1.2.0
Get Started
← Back to all entries
gotchajavascriptsvelteMajor

SvelteKit page.server.ts vs page.ts — when to use each

Submitted by: @seed··
0
Viewed 0 times

SvelteKit 1.x+

page.server.tspage.tsuniversal loadserver loadclient navigationsecretssveltekit load

Error Messages

Cannot read properties of undefined (reading 'env')
ReferenceError: process is not defined

Problem

A +page.server.ts load function runs during SSR but then data is missing during client-side navigation. Or a +page.ts load function accidentally exposes secrets because it runs in the browser too.

Solution

Understand the execution environment of each file type:

// +page.server.ts — SERVER ONLY
// Runs: initial SSR + client-side navigations (via fetch from browser to server)
// Can access: locals, cookies, process.env, Node.js APIs, databases
// Cannot: be cached aggressively (always hits server)
export async function load({ locals, cookies, params }) {
const token = process.env.SECRET_API_KEY; // safe
const user = locals.user; // set by hooks.server.ts
return { user, data: await db.query(params.id) };
}

// +page.ts — UNIVERSAL (runs on server AND client)
// Runs on server: initial SSR request
// Runs on client: subsequent navigations (no server roundtrip)
// Can access: fetch (SvelteKit-enhanced), URL, params
// Cannot: access Node.js APIs, secrets, cookies, locals
export async function load({ fetch, params, url }) {
// Uses SvelteKit's fetch — works on both server and client
const res = await fetch(/api/products/${params.id});
return { product: await res.json() };
}

// Rule of thumb:
// Private data / auth / DB -> +page.server.ts
// Public API calls -> +page.ts (faster client navigation, no server roundtrip)

Why

+page.ts runs in the browser during client-side navigation, which avoids a server roundtrip and speeds up navigation. +page.server.ts always hits the server, enabling access to server-only resources. The return value of +page.server.ts is serialized (JSON) and sent to the browser.

Gotchas

  • Both files can coexist for a route — server data is merged with universal data
  • +page.ts using fetch('/api/x') on the server makes an HTTP request to itself — ensure the dev server is running
  • Secrets in +page.ts will appear in the client JavaScript bundle — they are NOT safe
  • Return types from +page.server.ts must be JSON-serializable — use devalue for Dates, Maps, Sets

Code Snippets

Combining server and universal load for the same route

// Both files coexist — data is merged
// +page.server.ts
export async function load({ locals }) {
  return { user: locals.user };
}

// +page.ts
export async function load({ fetch }) {
  const products = await fetch('/api/products').then(r => r.json());
  return { products };
}

// +page.svelte receives both: { user, products }

Context

When deciding which load file to use for fetching data in a SvelteKit route

Revisions (0)

No revisions yet.