sveltekit
SKILL.md
SvelteKit Core Knowledge
Full Reference: See advanced.md for WebSocket integration, Server-Sent Events, Socket.IO, hybrid form actions, room management, and streaming patterns.
Deep Knowledge: Use
mcp__documentation__fetch_docswith technology:sveltekitfor comprehensive documentation.
Route Structure
src/routes/
├── +page.svelte → / (page UI)
├── +page.server.ts → / (server load/actions)
├── +layout.svelte → (shared layout)
├── users/
│ ├── +page.svelte → /users
│ └── [id]/
│ └── +page.svelte → /users/:id
Key Concepts
+page.svelte→ UI component+page.server.ts→ Server-only code+page.ts→ Universal (runs both)+layout→ Nested layouts+error.svelte→ Error pages
Load Functions
// +page.server.ts
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params, fetch }) => {
const user = await db.users.find(params.id);
return { user };
};
<!-- +page.svelte -->
<script lang="ts">
import type { PageData } from './$types';
export let data: PageData;
</script>
<h1>{data.user.name}</h1>
Form Actions
// +page.server.ts
import type { Actions } from './$types';
export const actions: Actions = {
default: async ({ request }) => {
const data = await request.formData();
await db.users.create({ name: data.get('name') });
return { success: true };
},
delete: async ({ params }) => {
await db.users.delete(params.id);
}
};
<form method="POST">
<input name="name" />
<button>Create</button>
</form>
<form method="POST" action="?/delete">
<button>Delete</button>
</form>
Form Validation with Zod
// src/routes/users/+page.server.ts
import { fail } from '@sveltejs/kit';
import { z } from 'zod';
const UserSchema = z.object({
email: z.string().email('Invalid email'),
name: z.string().min(2, 'Name too short'),
});
export const actions = {
create: async ({ request }) => {
const formData = await request.formData();
const data = Object.fromEntries(formData);
const result = UserSchema.safeParse(data);
if (!result.success) {
return fail(400, {
errors: result.error.flatten().fieldErrors,
data,
});
}
await createUser(result.data);
return { success: true };
},
};
<!-- +page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
export let form;
</script>
<form method="POST" action="?/create" use:enhance>
<input name="email" value={form?.data?.email ?? ''} />
{#if form?.errors?.email}
<span class="error">{form.errors.email[0]}</span>
{/if}
<button>Create</button>
</form>
Security Configuration
// svelte.config.js
export default {
kit: {
csrf: { checkOrigin: true },
csp: {
directives: {
'default-src': ['self'],
'script-src': ['self'],
},
},
},
};
// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
const response = await resolve(event);
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-Content-Type-Options', 'nosniff');
return response;
};
Authentication
// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';
import { redirect } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
const session = event.cookies.get('session');
if (session) {
event.locals.user = await verifySession(session);
}
// Protect routes
if (event.url.pathname.startsWith('/admin')) {
if (!event.locals.user?.isAdmin) {
throw redirect(303, '/login');
}
}
return resolve(event);
};
Error Handling
<!-- src/routes/+error.svelte -->
<script lang="ts">
import { page } from '$app/stores';
</script>
<div class="error">
<h1>{$page.status}</h1>
<p>{$page.error?.message}</p>
<a href="/">Go home</a>
</div>
// src/hooks.server.ts
import type { HandleServerError } from '@sveltejs/kit';
export const handleError: HandleServerError = async ({ error, event }) => {
const errorId = crypto.randomUUID();
console.error('Server error:', { errorId, error, url: event.url });
return {
message: 'An unexpected error occurred',
errorId,
};
};
Performance
// Prerendering
export const prerender = true;
export const entries = async () => {
const posts = await getPosts();
return posts.map((post) => ({ slug: post.slug }));
};
// Streaming
export const load: PageServerLoad = async () => {
return {
critical: await getCriticalData(),
streamed: {
slow: getSlowData(), // Not awaited
},
};
};
{#await data.streamed.slow}
<Skeleton />
{:then slowData}
<SlowContent data={slowData} />
{/await}
When NOT to Use This Skill
- Svelte without SvelteKit: Use
frontend-svelteskill - Next.js: Use
nextjs-app-routerskill - Nuxt: Use
nuxt3skill - Remix: Use
remixskill
Anti-Patterns
| Anti-Pattern | Why It's Wrong | Correct Approach |
|---|---|---|
| Fetching in onMount | Client-side only, no SSR | Use load function |
| Using +page.ts for secrets | Exposed to client | Use +page.server.ts |
| Not using use:enhance | No progressive enhancement | Add use:enhance to forms |
| Ignoring form validation | Security risk | Validate with Zod |
| No +error.svelte | Poor error UX | Create error page |
| Not setting CSRF protection | Security vulnerability | Enable csrf.checkOrigin |
Quick Troubleshooting
| Issue | Possible Cause | Solution |
|---|---|---|
| "Cannot access event.locals.user" | Not set in hooks | Set in handle hook |
| Form action not triggered | Missing action attribute | Use action="?/actionName" |
| Data undefined in load | Not exported | Export const load |
| CSRF error | checkOrigin enabled | Check csrf settings |
| Hydration error | Server/client differ | Use browser check |
| Redirect doesn't work | Wrong status code | Use redirect(303, '/path') |
Production Checklist
- CSP headers configured
- CSRF protection enabled
- Error handling (+error.svelte, hooks)
- Auth in hooks.server.ts
- Form validation with Zod
- use:enhance for progressive forms
- Prerendering static routes
- Streaming for slow data
Reference Documentation
Weekly Installs
9
Repository
claude-dev-suit…ev-suiteGitHub Stars
2
First Seen
9 days ago
Security Audits
Installed on
cursor8
gemini-cli8
amp8
cline8
github-copilot8
codex8