Add auth to a Svelte app in under 20 lines

One store, one load function, one redirect guard. Svelte makes this shorter than any other framework.

· LoginWith team

Svelte’s reactive model and server-side load functions make auth particularly clean. Here’s a minimal pattern that handles sign-in, sign-out, session persistence, and route protection in under 20 lines of application code.

The pieces

1. An auth store

// src/lib/auth.ts
import { writable } from 'svelte/store'

export type User = { id: string; email: string; name?: string } | null
export const user = writable<User>(null)

2. A server-side session hydration

// src/routes/+layout.server.ts
import type { LayoutServerLoad } from './$types'

export const load: LayoutServerLoad = async ({ cookies }) => {
  const sessionId = cookies.get('session')
  if (!sessionId) return { user: null }
  const user = await getUserFromSession(sessionId)
  return { user }
}

3. A client-side rehydration

<!-- src/routes/+layout.svelte -->
<script>
  import { onMount } from 'svelte'
  import { user } from '$lib/auth'
  export let data

  onMount(() => user.set(data.user))
</script>
<slot />

4. A redirect guard for protected routes

// src/routes/app/+layout.server.ts
import { redirect } from '@sveltejs/kit'
export const load = ({ parent }) => parent().then(({ user }) => {
  if (!user) throw redirect(302, '/login')
  return { user }
})

That’s it

Total line count: 18, not counting the getUserFromSession implementation (which is just a DB lookup).

The model:

  • SSR-first: the session is resolved server-side on every navigation, so the user is always up-to-date on page loads
  • Client store: the user is reactive in every component via $user
  • Protected routes: a single +layout.server.ts in any subtree enforces auth for everything under it

Sign-in flow

<script>
  async function signIn() {
    const res = await fetch('/api/auth/sign-in', {
      method: 'POST',
      body: JSON.stringify({ email, password }),
    })
    if (res.ok) {
      const { user: u } = await res.json()
      user.set(u)
      goto('/app')
    }
  }
</script>

The API route (src/routes/api/auth/sign-in/+server.ts) validates the credentials, creates a session, sets the cookie, returns the user. On client, we update the store and navigate.

Sign-out flow

<script>
  import { user } from '$lib/auth'
  import { goto } from '$app/navigation'

  async function signOut() {
    await fetch('/api/auth/sign-out', { method: 'POST' })
    user.set(null)
    goto('/')
  }
</script>

Why Svelte makes this shorter

  • Stores are primitive, don’t need a provider wrapper like React Context
  • Server-side load functions run on every navigation, so session hydration is automatic
  • Redirect from load is built into SvelteKit — no custom route-guard component needed
  • Cookies are first-class in load functions via the cookies parameter

None of this is unique to Svelte, but the ergonomics make it the shortest implementation you can write without sacrificing correctness.

Want auth that just works?

Get started with LoginWith