●WWDC — WWDC 2026 confirms Siri runs on Google Gemini; third-party handoff to ChatGPT is dropped, and Siri AI won't ship in the EU under the DMA at iOS 27●BILLING — 6 days until the Jun 15 change: Agent SDK, headless Claude Code, GitHub Actions, and third-party agents move to API-rate monthly credit●OUTAGE — claude.ai, Claude Code, and Cowork saw an outage (Jun). Scheduled runs are safest when built around fallbackModel and retries●DYNAMIC-WORKFLOWS — Dynamic workflows are on by default on Max/Team and the API, for codebase-wide bug hunts and independent verification●ULTRACODE — Claude Code's new ultracode setting sits in the effort menu, fixing effort to xhigh while Claude decides when to run a workflow●OPUS4.8 — Claude Opus 4.8 is settled in as the default across major plans, with stronger coding, agentic, and reasoning skills●WWDC — WWDC 2026 confirms Siri runs on Google Gemini; third-party handoff to ChatGPT is dropped, and Siri AI won't ship in the EU under the DMA at iOS 27●BILLING — 6 days until the Jun 15 change: Agent SDK, headless Claude Code, GitHub Actions, and third-party agents move to API-rate monthly credit●OUTAGE — claude.ai, Claude Code, and Cowork saw an outage (Jun). Scheduled runs are safest when built around fallbackModel and retries●DYNAMIC-WORKFLOWS — Dynamic workflows are on by default on Max/Team and the API, for codebase-wide bug hunts and independent verification●ULTRACODE — Claude Code's new ultracode setting sits in the effort menu, fixing effort to xhigh while Claude decides when to run a workflow●OPUS4.8 — Claude Opus 4.8 is settled in as the default across major plans, with stronger coding, agentic, and reasoning skills
Claude Code × SvelteKit Full-Stack Development Guide: From Zero to Production
A complete guide to full-stack development with Claude Code and SvelteKit. Covers Svelte 5 Runes, Drizzle ORM, Auth.js, and Cloudflare Workers deployment through practical, production-ready workflows.
In 2026's crowded web framework landscape, SvelteKit has carved out a distinctive position — and it deserves more attention from developers who use Claude Code as their primary AI assistant. SvelteKit's compile-time architecture eliminates unnecessary runtime overhead, producing JavaScript bundles that are dramatically smaller than React or Vue equivalents for the same feature set. Svelte 5's Runes syntax — $state, $derived, $effect, $props — brings a clarity to reactive programming that closely mirrors how developers think about data flow. And the first-class support for deploying to Cloudflare Workers and Vercel's edge network means your apps can run closer to users globally without the complexity of traditional server infrastructure.
Claude Code, meanwhile, has become more than an autocomplete engine. When given the right constraints — particularly a well-crafted CLAUDE.md file — it functions as an effective pair programmer: one that can implement boilerplate quickly, generate test code before implementation, and transform natural-language requirements into working TypeScript. The gap between having an idea and having a deployed, tested application narrows significantly when you combine these two tools.
This guide walks you through a real e-commerce project to demonstrate how Claude Code and SvelteKit work together across the full development lifecycle. By the end, you'll have a mental model for which tasks to hand off to Claude Code entirely, which tasks require careful human oversight, and how to set up the project so that Claude Code's output is consistently correct and production-ready from day one.
Chapter 1: Project Setup and CLAUDE.md Design
Initializing the SvelteKit Project
One of the first instincts developers have when using Claude Code is to ask it to scaffold the entire project. This is actually an area where you should stay in control. Technology selection decisions — which ORM to use, how to handle authentication, where to deploy — should be made by the developer, not delegated to an AI. Claude Code is excellent at implementing within a stack you've chosen, but it makes better decisions about implementation details when the architectural boundaries are already drawn.
Start by creating the SvelteKit project and installing your dependencies yourself:
# Create project (run this yourself before involving Claude Code)npm create svelte@latest my-sveltekit-app# → Select: TypeScript + ESLint + Prettier + Vitest + Playwrightcd my-sveltekit-app# Decide on your full dependency list before instructing Claude Codenpm install drizzle-orm @auth/sveltekitnpm install -D drizzle-kit wrangler
The selection prompts during create svelte matter. Choosing TypeScript strict mode from the start ensures Claude Code generates properly typed code throughout. Selecting Vitest and Playwright during initialization means the test configuration is ready immediately, letting you take a test-first approach with Claude Code from the first component.
Designing CLAUDE.md for SvelteKit
The CLAUDE.md file is the most important configuration you'll create for working with Claude Code on a SvelteKit project. Think of it as a contract between you and Claude Code — it defines the boundaries of what the AI should and should not do, and it prevents the most common sources of wasted effort: code generated with wrong patterns that needs to be rewritten.
For SvelteKit projects, there are three things CLAUDE.md must address explicitly. First, Svelte version: if you don't specify Svelte 5 Runes, Claude Code may default to Svelte 4 patterns since its training data contains both. Second, SSR safety: Cloudflare Workers and edge deployment mean there is no window object available during server-side rendering, and this catches many developers off guard. Third, code organization: specifying where components, stores, and server-side code should live prevents Claude Code from creating arbitrary file structures.
# CLAUDE.md — SvelteKit Project## Tech Stack- Framework: SvelteKit 2.x + Svelte 5 (Runes API)- Language: TypeScript (strict mode)- Database: Cloudflare D1 + Drizzle ORM- Auth: Auth.js v5 (@auth/sveltekit)- Styling: TailwindCSS v4- Test: Vitest (unit) + Playwright (e2e)- Deploy: Cloudflare Workers (via wrangler)## Coding Standards- Always use Svelte 5 Runes ($state, $derived, $effect, $props)- Never use legacy `export let` syntax- Always type the return value of load functions- Export Server Actions as an `actions` object- Components live under src/lib/components/- Page-specific components stay in the same directory as the route## Forbidden Patterns- Direct window/document access without a `browser` environment check (SSR safety)- Usage of the `any` type- Leaving console.log in production code## Testing Policy- Write Vitest unit tests for every component- Write Playwright E2E tests for form submissions and auth flows
With this CLAUDE.md in place, Claude Code defaults to Svelte 5 Runes syntax, wraps browser APIs in environment guards automatically, and maintains a consistent project structure across every file it creates. The quality difference compared to a project without CLAUDE.md is significant — expect to spend dramatically less time reviewing and correcting generated code.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦Developers stuck on the SvelteKit + Claude Code combination will be able to implement Svelte 5 Runes-based components, API routes, and database integrations starting today
✦You'll master type-safe implementation patterns using Drizzle ORM + Auth.js + Cloudflare D1 — the modern edge-first stack — with working code you can use immediately
✦You'll develop the judgment to know which tasks to delegate to Claude Code and which to review yourself, tripling your SvelteKit development velocity
Secure payment via Stripe · Cancel anytime
Chapter 2: Component Design with Svelte 5 Runes
Understanding What Runes Change
Before giving Claude Code component instructions, it helps to understand what Svelte 5 Runes fundamentally change. In Svelte 4, reactivity was implicit — the compiler detected reactive dependencies from how you wrote your code. In Svelte 5, reactivity is explicit through Runes: you declare what is reactive state with $state(), what is a computed value with $derived(), and what runs as a side effect with $effect(). This shift makes reactive code more predictable and easier for tools like Claude Code to generate correctly.
The $props() Rune replaces export let for component properties. This seemingly small change has a significant impact: props are now a single destructured object rather than individual exports, making the component interface cleaner and TypeScript-friendly in a way that aligns well with React developers' mental models. When instructing Claude Code, this is one area where being explicit pays off — specifying "use $props() to define props" leads to correct output even when the component is complex.
Prompt Patterns for Claude Code
The most effective Claude Code prompts for SvelteKit components combine three elements: a clear description of what the component renders, the full TypeScript interface for its props, and the behavior expectations for any interactive elements. Vague prompts like "create a product card" produce generic output; specific prompts produce production-ready components on the first attempt.
# Claude Code prompt example
"Create a ProductCard.svelte component with the following requirements:
- Props: product: { id: string; name: string; price: number; imageUrl: string }
- Use Svelte 5's $props()
- Manage local quantity state with $state()
- Calculate total price with $derived()
- Emit cart addition via an onaddtocart callback to the parent
- Style with TailwindCSS
- Include a Vitest test file as well"
Here's an example of the code Claude Code would generate from this prompt:
Notice how $derived eliminates the need for any explicit recalculation logic — the compiler handles dependency tracking automatically. When quantity changes, totalPrice updates without any manual triggering.
When to Use $effect — and When to Avoid It
$effect is Svelte 5's equivalent of React's useEffect, with automatic dependency tracking. The key insight for working with Claude Code is that $effect should only be used for side effects with external systems — subscribing to WebSockets, initializing third-party libraries, or synchronizing with browser APIs. When you find yourself writing $effect to derive a computed value, $derived is almost always the better choice.
Add these guidelines to your CLAUDE.md to keep Claude Code from misusing $effect:
// ✅ Good use case: synchronizing with external systems$effect(() => { const subscription = websocket.subscribe(channel, (data) => { messages = [...messages, data]; }); return () => subscription.unsubscribe(); // cleanup function prevents memory leaks});// ❌ Avoid: use $derived instead when computing derived values$effect(() => { filteredItems = items.filter(item => item.active); // $derived is cleaner and more efficient here});
The practical difference is significant: $derived is synchronous and computed lazily, while $effect runs asynchronously after the DOM updates. Using $effect for derived values introduces unnecessary rendering delays and makes the data flow harder to reason about.
Chapter 3: Type-Safe API Routes and Server Actions
How SvelteKit's Load System Works with Claude Code
SvelteKit's server-side data loading uses +page.server.ts files that export a load function. The framework automatically infers the TypeScript types for data returned from load — a feature that Claude Code can leverage to generate fully type-safe page components without any manual type annotations in the component itself.
The key to getting high-quality output from Claude Code for server files is providing context about the data model and the environment. SvelteKit running on Cloudflare Workers accesses environment bindings through event.platform?.env, which is a pattern Claude Code needs to know about to generate correct code. Your CLAUDE.md should specify this, or include it in individual prompts for server-side files:
# Claude Code prompt
"Create src/routes/products/+page.server.ts:
- A load function that fetches from the products table via Drizzle ORM
- Category filter support via ?category= URL param
- A Server Action that lets authenticated users add items to cart
- Use PageServerLoad and Actions types correctly
- Database is event.platform?.env?.DB (Cloudflare D1)"
Delegating Drizzle ORM Schema Design to Claude Code
Database schema design is one of Claude Code's strong suits for SvelteKit projects. Unlike application code where the behavior emerges from multiple interacting files, a schema is self-contained enough that Claude Code can produce high-quality output from a natural-language description. Describe your entities and their relationships in plain English, and Claude Code will generate a well-structured Drizzle schema with appropriate indexes and foreign key constraints:
One thing to always review in Claude Code's schema output: the cascade behavior on foreign keys. onDelete: 'cascade' is often the right choice for associative tables like cartItems, but for tables that should retain data for audit purposes after a parent record is deleted, you want onDelete: 'set null' or no cascade at all. This is a business logic decision that Claude Code cannot make without your input.
Chapter 4: Auth.js v5 Authentication
Setting Up Auth.js with Cloudflare D1
Auth.js v5 has official SvelteKit support and works well with Claude Code, but the configuration structure changed significantly from v4. When giving Claude Code auth-related instructions, always specify the major version number — the difference between v4 and v5 config patterns is substantial enough that Claude Code without clear version guidance may generate code for the wrong version.
The key things to specify: the adapter (DrizzleAdapter for D1-backed sessions), the session strategy (database sessions are more secure than JWT for most applications), and the need for module augmentation to extend the locals type. Without that last item, TypeScript will reject every call to locals.auth() across your server files:
// src/auth.ts (generated by Claude Code)import { SvelteKitAuth } from '@auth/sveltekit';import { DrizzleAdapter } from '@auth/drizzle-adapter';import GitHub from '@auth/core/providers/github';import Resend from '@auth/core/providers/resend';import { db } from '$lib/server/db';import { env } from '$env/dynamic/private';export const { handle, auth, signIn, signOut } = SvelteKitAuth({ adapter: DrizzleAdapter(db), providers: [ GitHub({ clientId: env.GITHUB_CLIENT_ID, clientSecret: env.GITHUB_CLIENT_SECRET, }), Resend({ apiKey: env.RESEND_API_KEY, from: 'noreply@yourdomain.com', }), ], session: { strategy: 'database' }, callbacks: { session({ session, user }) { // Attach the database user.id to the session object session.user.id = user.id; return session; }, },});
Route Guards and Protected Areas
SvelteKit's +layout.server.ts pattern lets you protect entire route groups with a single file. Any route nested under a (protected) group directory will be covered by the layout's load function, which can redirect unauthenticated users before any page-level data fetching begins:
// src/routes/(protected)/+layout.server.tsimport type { LayoutServerLoad } from './$types';import { redirect } from '@sveltejs/kit';export const load: LayoutServerLoad = async ({ locals }) => { const session = await locals.auth(); if (!session) { redirect(303, '/login'); } return { session };};
The advantage of this pattern over per-page guards is consistency: you cannot accidentally forget to protect a new page added to the protected group, because the protection is applied at the layout level automatically. Claude Code will generate this pattern correctly when you specify "protect the entire (protected) route group" in your instructions.
Deploying SvelteKit to Cloudflare Workers requires understanding Cloudflare's resource binding system. Unlike traditional server deployments where environment variables are simple strings, Cloudflare uses bindings to connect your Worker to D1 databases, KV namespaces, R2 buckets, and other platform resources. These bindings are declared in wrangler.toml and accessed in your application through event.platform.env.
The critical thing to understand before having Claude Code generate your wrangler.toml is that binding names in wrangler.toml must match what your code uses. If your code references event.platform.env.DB, the binding in wrangler.toml must also be named DB. Claude Code handles this consistency well when you review the generated file carefully:
# wrangler.toml (generated by Claude Code — review binding IDs carefully)name = "my-sveltekit-app"compatibility_date = "2025-11-01"compatibility_flags = ["nodejs_compat"][[d1_databases]]binding = "DB"database_name = "my-app-db"database_id = "YOUR_D1_DATABASE_ID" # Replace with actual ID from Cloudflare dashboard[[kv_namespaces]]binding = "SESSION_KV"id = "YOUR_KV_NAMESPACE_ID" # Replace with actual ID[vars]PUBLIC_APP_NAME = "My SvelteKit App"
The database_id and id values are the one thing Claude Code cannot fill in — they're unique identifiers from your Cloudflare account. Everything else in the generated wrangler.toml should be correct.
Automated Deploy Script
Claude Code can create a deployment script that chains migration, build, and deploy in a single command. This is particularly valuable for avoiding the common mistake of forgetting to run migrations before deploying application code:
#!/bin/bash# scripts/deploy.sh (generated by Claude Code)set -eecho "🔄 Running DB migrations..."npx drizzle-kit migrate --config=drizzle.config.tsecho "🏗️ Building..."npm run buildecho "🚀 Deploying to Cloudflare Workers..."npx wrangler deployecho "✅ Deployment complete!"
The set -e flag is important — it ensures the script stops if any command fails. Without it, a failed migration would be silently ignored and the deploy would proceed with an out-of-date database schema.
Chapter 6: Testing Strategy with Vitest and Playwright
Why Test-First Works Particularly Well with Claude Code
The test-first development approach — writing tests before implementation — has always been valuable in theory but difficult to maintain in practice. Claude Code changes this equation. The overhead of writing tests first disappears when you can simply describe your component's behavior and ask Claude Code to generate both the test and the implementation.
The deeper benefit is architectural: when you give Claude Code a test spec before asking for implementation, it is forced to treat the component as a black box with clearly defined inputs and outputs. This naturally leads to better separation of concerns and more testable code. The spec you provide is the design document, and the tests are the specification. Working this way consistently produces code that is easier to refactor and extend later.
# Claude Code prompt (TDD approach)
"Write Vitest tests for CartStore first.
Spec:
- addItem(product, quantity): adds to cart, increments quantity for existing items
- removeItem(productId): removes item from cart
- clearCart(): empties the cart
- totalPrice: computed total ($derived)
- itemCount: total quantity across all items ($derived)
Then write the implementation that makes all tests pass."
End-to-end tests for checkout flows and authentication are where Claude Code's output requires the most human review, because E2E tests make assumptions about the DOM structure and URL routing of your application. Before asking Claude Code to write E2E tests, make sure your component data-testid attributes follow a consistent naming scheme — this gives Claude Code reliable selectors to work with:
// tests/checkout.spec.ts (generated by Claude Code)import { test, expect } from '@playwright/test';test.describe('Checkout Flow', () => { test('adds a product to cart and proceeds to checkout', async ({ page }) => { await page.goto('/products'); await page.getByTestId('product-card').first() .getByRole('button', { name: 'Add to Cart' }).click(); await page.goto('/cart'); await expect(page.getByTestId('cart-item')).toHaveCount(1); await page.getByRole('button', { name: 'Proceed to Checkout' }).click(); await expect(page).toHaveURL('/checkout'); });});
For authentication-related E2E tests, the standard approach is to use Playwright's storageState feature to inject a pre-authenticated session, bypassing the actual OAuth flow during tests. Claude Code knows this pattern and will generate the appropriate test fixture setup when asked.
Chapter 7: Common Errors and How Claude Code Handles Them
Error 1: window is not defined (SSR Errors)
This is the most common error developers encounter when starting SvelteKit, and it catches even experienced React developers off guard. In SvelteKit, your code runs on both the server (during SSR) and the client. The window, document, and localStorage objects simply do not exist in the server environment. Claude Code will generate SSR-safe code automatically when CLAUDE.md specifies SSR safety, but you can also address it in individual prompts by adding "write SSR-safe code":
// ❌ Causes the error — window is not defined on serverconst stored = window.localStorage.getItem('theme');// ✅ SSR-safe code (what Claude Code generates)import { browser } from '$app/environment';const stored = browser ? window.localStorage.getItem('theme') : null;// ✅ Alternative: use SvelteKit's $app/stores for browser-only codeimport { page } from '$app/stores';
Error 2: Drizzle ORM Import Path Errors for Cloudflare D1
When using Drizzle with Cloudflare D1, you need drizzle-orm/d1 — not better-sqlite3. This is a non-obvious difference that trips up developers migrating from local SQLite development to Cloudflare D1. Include this in your CLAUDE.md or mention it explicitly when asking Claude Code to generate database utility files:
// src/lib/server/db.ts — correct import for Cloudflare D1import { drizzle } from 'drizzle-orm/d1'; // ✅ D1-specific driverimport * as schema from './schema';export function createDb(d1: D1Database) { return drizzle(d1, { schema });}// In hooks.server.ts — attach to the request eventexport const handle: Handle = sequence(authHandle, async ({ event, resolve }) => { event.locals.db = createDb(event.platform?.env?.DB); return resolve(event);});
Error 3: Auth.js Session TypeScript Errors
Without module augmentation, TypeScript will report errors on every locals.auth() call and every access to session.user.id. This is a one-time setup that Claude Code should generate as part of the auth configuration, but it's easy to omit. Add this to src/app.d.ts:
SvelteKit's @sveltejs/enhanced-img package provides automatic WebP conversion, responsive sizes, and lazy loading — everything you'd normally configure manually in an image optimization pipeline. The enhancement happens at build time, so there's no runtime cost. Ask Claude Code to use <enhanced:img> instead of <img> for all static images in your project:
For dynamically-loaded images (from a database or CMS), enhanced:img is not applicable, but you can use the srcset attribute directly to provide multiple resolutions. Claude Code can generate this automatically when you specify that the image URL is dynamic.
Strategic Link Preloading
SvelteKit's preloading system lets you control when the browser fetches linked pages. For product listing pages where users are likely to click into individual products, hover preloading provides near-instant navigation without wasting bandwidth on links that are never clicked:
<!-- Preload on hover — balances performance and bandwidth --><a href="/products/{product.id}" data-sveltekit-preload-data="hover"> {product.name}</a>
For navigation items that are almost always clicked (like the main CTA), data-sveltekit-preload-data="eager" preloads immediately. Claude Code will suggest the appropriate preload strategy when you describe the usage pattern of a link.
Caching Headers in Cloudflare Workers
Setting cache control headers correctly is one of the highest-impact optimizations for a SvelteKit app on Cloudflare Workers. Public pages like product listings can be served from Cloudflare's edge cache without hitting your Worker at all for repeat visitors, dramatically reducing both latency and cost:
// src/routes/products/+page.server.tsexport const load: PageServerLoad = async ({ url, setHeaders }) => { // Cache product listing for 60 seconds at both browser and CDN level setHeaders({ 'cache-control': 'public, max-age=60, s-maxage=60', }); // ... data fetching};
One important caveat: pages with user-specific data (cart contents, user profiles) should never be cached at the CDN level. Make sure Claude Code understands which pages are public and which are personalized when generating cache headers.
A Note from an Indie Developer
Key Takeaways
The Claude Code × SvelteKit combination delivers genuine productivity gains when configured correctly. The investment in setup — primarily writing a thorough CLAUDE.md and committing to test-first development — pays back quickly in the form of consistently correct, production-ready code.
The core mental model for this workflow: CLAUDE.md defines what is always true about your project; your prompts define what you need right now. When both are specific, Claude Code's output is reliable enough to commit with minimal review for most application code. When either is vague, expect to spend time correcting the output.
The Cloudflare Workers + D1 + Drizzle ORM + Auth.js stack is worth adopting alongside Claude Code because it's a stack where the deployment target, data model, and authentication system are well-defined enough that Claude Code can generate correct cross-cutting code — the database hooks are consistent with the schema, the auth setup is consistent with the session model, and the deployment config is consistent with what the code expects from the environment.
Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.