SvelteKit AI Rules

Rules for SvelteKit projects with Svelte 5 runes: data-loading conventions, form actions, page-level vs component-level state, and adapter setup. Updates the AI from Svelte 4 patterns.

TypeScriptSvelteKit#svelte#sveltekit#svelte-5#runesLast updated 2026-05-05
tune

Want to customize this rules file? Open the generator with this stack pre-loaded.

Open in generatorarrow_forward

Save at .cursor/rules/main.mdc

SvelteKit

Project context

This is a SvelteKit project using Svelte 5 with the runes API. AI assistants frequently default to Svelte 4 patterns ($:, $$props); these rules exist to keep the codebase modern.

Stack

  • SvelteKit 2+
  • Svelte 5+ with runes ($state, $derived, $effect, $props, $bindable)
  • TypeScript strict
  • Tailwind CSS 4
  • Vite (built-in to SvelteKit)
  • Vitest + Playwright
  • pnpm

Folder structure

src/
  app.html
  app.d.ts
  hooks.server.ts        — server hooks (auth, error handling)
  hooks.client.ts        — client hooks (rare)
  lib/                   — code reusable across routes; aliased as $lib
    server/              — code that must never ship to the browser
  routes/
    +layout.svelte
    +layout.ts           — shared load
    +page.svelte
    +page.ts             — universal load
    +page.server.ts      — server-only load
    +server.ts           — API endpoints
    +error.svelte
    blog/[slug]/
      +page.svelte
      +page.server.ts
static/                  — public assets

Svelte 5 runes (use these, not Svelte 4 syntax)

  • $state(...) — reactive local state (replaces let foo)
  • $derived(...) — computed values (replaces $:)
  • $effect(() => { ... }) — side effects (replaces $: for effects)
  • $props() — declares props (replaces export let)
  • $bindable() — for two-way bindings on props
<script lang="ts">
  let { initial = 0, onchange } = $props<{ initial?: number; onchange?: (n: number) => void }>()
  let count = $state(initial)
  let doubled = $derived(count * 2)

  $effect(() => {
    onchange?.(count)
  })
</script>

<button onclick={() => count++}>Count: {count} (×2 = {doubled})</button>

Loading data

  • +page.ts (universal) — runs on server then client; for public, cacheable data
  • +page.server.ts (server only) — for DB calls, secrets, anything server-bound
  • Return data from load(); SvelteKit hydrates data prop on +page.svelte
  • Use depends('app:foo') + invalidate('app:foo') for fine-grained re-fetching
  • Use parent() to inherit data from layout loads — don't refetch

Form actions

// +page.server.ts
import type { Actions } from './$types'

export const actions: Actions = {
  default: async ({ request, locals }) => {
    const data = await request.formData()
    // ... process, then redirect / return error / return success
  },
}
  • Use form actions for mutations — not raw fetch() from client
  • Validate inputs at the server boundary (Zod / valibot)
  • Use fail(400, { message }) for validation errors
  • Use redirect(303, '/path') for post-success navigation

API endpoints (+server.ts)

Only use +server.ts for true API endpoints (consumed by external clients, webhooks). For internal mutations, use form actions.

Adapters

  • @sveltejs/adapter-vercel for Vercel
  • @sveltejs/adapter-cloudflare for Cloudflare Pages / Workers
  • @sveltejs/adapter-node for Node servers
  • @sveltejs/adapter-static for fully static sites
  • Pick one in svelte.config.js; don't ship multiple

Patterns to follow

  • Co-locate everything in src/routes+page.svelte, its load, its server-only logic
  • Prefix server-only modules with $lib/server/ or use +page.server.ts
  • Use app.d.ts to type App.Locals (request-scoped data) and App.Error
  • Use <form method="POST"> for mutations — progressive enhancement

Patterns to avoid

  • export let foo — use $props()
  • $: reactive statements — use $derived or $effect
  • $$props, $$restProps — use $props() and rest spread
  • Storing fetched data in let — use data from a load function
  • Calling fetch in onMount — use a load function instead
  • Mutating store values directly across components — use writable / runes deliberately

Testing

  • Vitest for unit tests (component tests via @testing-library/svelte)
  • Playwright for end-to-end
  • Use $lib/server/test-helpers.ts for test fixtures (will not bundle to browser)

Tooling

  • pnpm dev — Vite dev server
  • pnpm build — production build (uses adapter)
  • pnpm preview — preview built output
  • pnpm test — Vitest
  • pnpm test:e2e — Playwright
  • pnpm checksvelte-check --tsconfig ./tsconfig.json

AI behavioral rules

  • Default to Svelte 5 runes; never propose let foo for reactive state
  • Don't suggest $: — use $derived for values, $effect for side effects
  • Don't suggest export let — use $props()
  • For mutations, use form actions; don't propose client-side fetch as the default
  • Put server-only code in $lib/server/ or +page.server.ts so it never bundles to the browser
  • For data fetching in pages, use a load function — never onMount + fetch
  • Run pnpm check and pnpm test before declaring a task done

Frequently asked

How do I use this SvelteKit rules file with Cursor?

Pick "Cursor (.cursor/rules/*.mdc)" from the format dropdown above and click Copy. Save it at .cursor/rules/main.mdc in your project root and restart Cursor. The legacy .cursorrules format still works if you're on an older Cursor version — pick that option instead.

Can I use this with Claude Code (CLAUDE.md)?

Yes — pick "Claude Code (CLAUDE.md)" from the format dropdown above and copy. Save the file as CLAUDE.md at your repo root. Claude Code reads it automatically on every session. For monorepos, you can also drop nested CLAUDE.md files in subdirectories — Claude merges them when working in those paths.

Where exactly do I put this file?

It depends on the AI tool. Cursor reads .cursorrules or .cursor/rules/*.mdc at the project root. Claude reads CLAUDE.md at the project root. Copilot reads .github/copilot-instructions.md. The "Save at" path under each format in the dropdown shows the exact location for the format you picked.

Can I customize these SvelteKit rules for my project?

Yes — that's what the generator is for. Click "Open in generator" above and the wizard loads with this stack's defaults pre-selected. Toggle on or off the conventions you want, then re-export in your AI tool's format.

Will using this rules file slow down my AI tool?

No. Rules files count toward the model's context window but not toward latency in any noticeable way. The file is loaded once per session, not per token. The library files target 250–400 lines, well within every tool's recommended budget.

Should I commit this file to git?

Yes. The rules file is project documentation that benefits every developer using the AI tool. Commit it. The exception is personal-global settings (e.g. ~/.claude/CLAUDE.md) which are user-scoped and stay out of the repo.

Related stacks