SvelteKit¶
SvelteKit integration with Litestar Vite for full-stack Svelte applications.
Quick Start¶
litestar assets init --template sveltekit
This creates a SvelteKit project with TypeScript support and SSR capabilities.
Project Structure¶
SvelteKit applications use a specialized Vite plugin:
my-app/
├── app.py # Litestar backend
├── package.json
├── vite.config.ts # Vite + SvelteKit plugin
├── svelte.config.js # SvelteKit configuration
├── tsconfig.json
├── src/
│ ├── app.html # HTML template
│ ├── routes/
│ │ └── +page.svelte # Pages (file-based routing)
│ └── lib/
│ └── api/ # Generated types from OpenAPI
└── build/ # SvelteKit build output
Backend Setup¶
"""SvelteKit example - shared "Library" backend for SvelteKit SSR frontend.
SvelteKit runs as an SSR server. In dev mode, Litestar proxies all non-API
routes to the SvelteKit dev server. In production, SvelteKit's `hooks.server.ts`
proxies /api/* requests to the Litestar backend.
All examples in this repository expose the same backend:
- `/api/summary` - overview + featured book
- `/api/books` - list of books
- `/api/books/{book_id}` - single book
Dev mode (default):
litestar --app-dir examples/sveltekit run
Production (two terminals):
# Terminal 1: Install deps, build, and serve SvelteKit SSR server
litestar --app-dir examples/sveltekit assets install
litestar --app-dir examples/sveltekit assets build
litestar --app-dir examples/sveltekit assets serve
# Terminal 2: Litestar API server
VITE_DEV_MODE=false litestar --app-dir examples/sveltekit run
The SvelteKit server includes `hooks.server.ts` which proxies /api/* requests
to the Litestar backend (configurable via LITESTAR_API env var, default: localhost:8000).
"""
import os
from pathlib import Path
from litestar import Controller, Litestar, get
from litestar.exceptions import NotFoundException
from msgspec import Struct
from litestar_vite import PathConfig, RuntimeConfig, TypeGenConfig, ViteConfig, VitePlugin
here = Path(__file__).parent
DEV_MODE = os.getenv("VITE_DEV_MODE", "true").lower() in {"true", "1", "yes"}
class Book(Struct):
id: int
title: str
author: str
year: int
tags: list[str]
class Summary(Struct):
app: str
headline: str
total_books: int
featured: Book
BOOKS: list[Book] = [
Book(id=1, title="Async Python", author="C. Developer", year=2024, tags=["python", "async"]),
Book(id=2, title="Type-Safe Web", author="J. Dev", year=2025, tags=["typescript", "api"]),
Book(id=3, title="Frontend Patterns", author="A. Designer", year=2023, tags=["frontend", "ux"]),
]
def _get_book(book_id: int) -> Book:
for book in BOOKS:
if book.id == book_id:
return book
raise NotFoundException(detail=f"Book {book_id} not found")
def _get_summary() -> Summary:
"""Build summary data."""
return Summary(
app="litestar-vite library", headline="One backend, many frontends", total_books=len(BOOKS), featured=BOOKS[0]
)
class LibraryController(Controller):
"""Library API controller."""
@get("/api/summary")
async def summary(self) -> Summary:
"""Overview endpoint used across all examples."""
return _get_summary()
@get("/api/books")
async def books(self) -> list[Book]:
"""Return all books."""
return BOOKS
@get("/api/books/{book_id:int}")
async def book_detail(self, book_id: int) -> Book:
"""Return a single book by id."""
return _get_book(book_id)
vite = VitePlugin(
config=ViteConfig(
mode="ssr", # SSR mode: proxy in dev, Node serves HTML in prod
dev_mode=DEV_MODE,
paths=PathConfig(root=here),
types=TypeGenConfig(output=Path("src/lib/generated"), generate_zod=True),
# Fixed port for E2E tests - can be removed for local dev or customized for production
runtime=RuntimeConfig(port=5022),
)
)
app = Litestar(route_handlers=[LibraryController], plugins=[vite], debug=True)
Key points:
mode="framework"enables meta-framework integration mode (aliases:mode="ssr"/mode="ssg")ExternalDevServerdelegates dev server to SvelteKitTypeGenConfigenables type generation for SvelteKit
Vite Configuration¶
import { sveltekit } from "@sveltejs/kit/vite"
import tailwindcss from "@tailwindcss/vite"
import { litestarSvelteKit } from "litestar-vite-plugin/sveltekit"
import { defineConfig } from "vite"
// Litestar manages the dev server port via VITE_PORT and runtime config.
// The SvelteKit plugin reads the port automatically - no hardcoding needed.
// LITESTAR_PORT is the backend API server port (default 8000).
const LITESTAR_PORT = process.env.LITESTAR_PORT ?? "8000"
export default defineConfig({
plugins: [
tailwindcss(),
litestarSvelteKit({
apiProxy: `http://localhost:${LITESTAR_PORT}`,
apiPrefix: "/api",
}),
sveltekit(),
],
})
The litestarSvelteKit plugin must come before the sveltekit() plugin.
Configuration options:
apiProxy- URL of Litestar backend (default:http://localhost:8000)apiPrefix- API route prefix to proxy (default:/api)types- Enable type generation (reads from.litestar.jsonor explicit config)
API Integration¶
Using Load Functions¶
SvelteKit’s load functions are perfect for SSR data fetching:
// src/routes/users/+page.ts
import type { PageLoad } from './$types'
import type { User } from '$lib/api/types.gen'
import { route } from '$lib/api/routes'
export const load: PageLoad = async ({ fetch }) => {
const response = await fetch(route('users:list'))
const users: User[] = await response.json()
return { users }
}
<!-- src/routes/users/+page.svelte -->
<script lang="ts">
import type { PageData } from './$types'
export let data: PageData
</script>
<h1>Users</h1>
<ul>
{#each data.users as user (user.id)}
<li>{user.name}</li>
{/each}
</ul>
Server-Side API Calls¶
Use +page.server.ts for server-only logic:
// src/routes/users/+page.server.ts
import type { PageServerLoad } from './$types'
import type { User } from '$lib/api/types.gen'
export const load: PageServerLoad = async ({ fetch }) => {
const response = await fetch('http://localhost:8000/api/users')
const users: User[] = await response.json()
return { users }
}
Client-Side Fetch¶
<script lang="ts">
import { onMount } from 'svelte'
import type { Summary } from '$lib/api/types.gen'
import { route } from '$lib/api/routes'
let summary = $state<Summary | null>(null)
onMount(async () => {
const res = await fetch(route('summary'))
summary = await res.json()
})
</script>
{#if summary}
<h1>{summary.headline}</h1>
{/if}
Running¶
# Recommended: Litestar manages both servers
litestar run --reload
# Alternative: Run separately
litestar assets serve --production # SvelteKit server
litestar run --reload # Backend API (in another terminal)
Type Generation¶
With types=TypeGenConfig() enabled in Python:
litestar assets generate-types
This generates TypeScript types in src/lib/api/ (configured via .litestar.json).
The types are accessible via SvelteKit’s $lib alias:
import type { User } from '$lib/api/types.gen'
import { route } from '$lib/api/routes'
Deployment¶
For production:
Build SvelteKit:
litestar assets buildServe both apps:
litestar assets serve --production # SvelteKit adapter server litestar run # Backend API
The build uses SvelteKit’s adapter (e.g., @sveltejs/adapter-node for Node.js deployment).
See Also¶
Type Generation - TypeScript type generation