sveltekit

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

SvelteKit Core Knowledge

SvelteKit 核心知识

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_docs
with technology:
sveltekit
for comprehensive documentation.
完整参考:WebSocket 集成、Server-Sent Events、Socket.IO、混合 form actions、房间管理和流式传输模式相关内容请查看 advanced.md
深度知识:如需获取完整文档,请调用
mcp__documentation__fetch_docs
并指定 technology 参数为
sveltekit

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
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

  • +page.svelte
    → UI 组件
  • +page.server.ts
    → 仅服务端运行的代码
  • +page.ts
    → 通用代码(客户端和服务端均会运行)
  • +layout
    → 嵌套布局
  • +error.svelte
    → 错误页面

Load Functions

Load Functions

ts
// +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 };
};
svelte
<!-- +page.svelte -->
<script lang="ts">
  import type { PageData } from './$types';
  export let data: PageData;
</script>

<h1>{data.user.name}</h1>

ts
// +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 };
};
svelte
<!-- +page.svelte -->
<script lang="ts">
  import type { PageData } from './$types';
  export let data: PageData;
</script>

<h1>{data.user.name}</h1>

Form Actions

Form Actions

ts
// +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);
  }
};
svelte
<form method="POST">
  <input name="name" />
  <button>Create</button>
</form>

<form method="POST" action="?/delete">
  <button>Delete</button>
</form>
ts
// +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);
  }
};
svelte
<form method="POST">
  <input name="name" />
  <button>创建</button>
</form>

<form method="POST" action="?/delete">
  <button>删除</button>
</form>

Form Validation with Zod

基于 Zod 的表单验证

typescript
// 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 };
  },
};
svelte
<!-- +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>

typescript
// src/routes/users/+page.server.ts
import { fail } from '@sveltejs/kit';
import { z } from 'zod';

const UserSchema = z.object({
  email: z.string().email('无效邮箱地址'),
  name: z.string().min(2, '名称过短'),
});

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 };
  },
};
svelte
<!-- +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>创建</button>
</form>

Security Configuration

安全配置

typescript
// 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;
};

typescript
// 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

身份认证

typescript
// 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);
};

typescript
// 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);
  }

  // 路由保护
  if (event.url.pathname.startsWith('/admin')) {
    if (!event.locals.user?.isAdmin) {
      throw redirect(303, '/login');
    }
  }

  return resolve(event);
};

Error Handling

错误处理

svelte
<!-- 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>
typescript
// 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,
  };
};

svelte
<!-- 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="/">返回首页</a>
</div>
typescript
// src/hooks.server.ts
import type { HandleServerError } from '@sveltejs/kit';

export const handleError: HandleServerError = async ({ error, event }) => {
  const errorId = crypto.randomUUID();
  console.error('服务端错误:', { errorId, error, url: event.url });

  return {
    message: '发生了意外错误',
    errorId,
  };
};

Performance

性能优化

typescript
// 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
    },
  };
};
svelte
{#await data.streamed.slow}
  <Skeleton />
{:then slowData}
  <SlowContent data={slowData} />
{/await}

typescript
// 预渲染
export const prerender = true;

export const entries = async () => {
  const posts = await getPosts();
  return posts.map((post) => ({ slug: post.slug }));
};

// 流式传输
export const load: PageServerLoad = async () => {
  return {
    critical: await getCriticalData(),
    streamed: {
      slow: getSlowData(), // 不等待执行完成
    },
  };
};
svelte
{#await data.streamed.slow}
  <Skeleton />
{:then slowData}
  <SlowContent data={slowData} />
{/await}

When NOT to Use This Skill

本技能不适用场景

  • Svelte without SvelteKit: Use
    frontend-svelte
    skill
  • Next.js: Use
    nextjs-app-router
    skill
  • Nuxt: Use
    nuxt3
    skill
  • Remix: Use
    remix
    skill
  • 不含 SvelteKit 的纯 Svelte 开发:使用
    frontend-svelte
    技能
  • Next.js 开发:使用
    nextjs-app-router
    技能
  • Nuxt 开发:使用
    nuxt3
    技能
  • Remix 开发:使用
    remix
    技能

Anti-Patterns

反模式

Anti-PatternWhy It's WrongCorrect Approach
Fetching in onMountClient-side only, no SSRUse load function
Using +page.ts for secretsExposed to clientUse +page.server.ts
Not using use:enhanceNo progressive enhancementAdd use:enhance to forms
Ignoring form validationSecurity riskValidate with Zod
No +error.sveltePoor error UXCreate error page
Not setting CSRF protectionSecurity vulnerabilityEnable csrf.checkOrigin
反模式问题原因正确方案
在 onMount 中发起请求仅客户端执行,无 SSR 效果使用 load function
在 +page.ts 中存放敏感信息会暴露给客户端使用 +page.server.ts
不使用 use:enhance无渐进式增强能力给表单添加 use:enhance
忽略表单验证存在安全风险使用 Zod 做验证
没有 +error.svelte用户错误体验差创建错误页面
不开启 CSRF 防护存在安全漏洞启用 csrf.checkOrigin

Quick Troubleshooting

快速故障排查

IssuePossible CauseSolution
"Cannot access event.locals.user"Not set in hooksSet in handle hook
Form action not triggeredMissing action attributeUse action="?/actionName"
Data undefined in loadNot exportedExport const load
CSRF errorcheckOrigin enabledCheck csrf settings
Hydration errorServer/client differUse browser check
Redirect doesn't workWrong status codeUse redirect(303, '/path')
问题可能原因解决方案
"Cannot access event.locals.user"未在 hooks 中设置在 handle 钩子中配置
Form action 未触发缺少 action 属性使用 action="?/actionName"
load 中数据为 undefined未导出配置导出 const load
CSRF 错误checkOrigin 已开启检查 csrf 配置
hydration 错误服务端/客户端渲染结果不一致增加浏览器环境判断
重定向不生效状态码错误使用 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
  • 已配置 CSP 头
  • 已开启 CSRF 防护
  • 已配置错误处理(+error.svelte、hooks)
  • 已在 hooks.server.ts 中实现身份认证
  • 已使用 Zod 做表单验证
  • 已为表单添加 use:enhance 实现渐进式增强
  • 已为静态路由配置预渲染
  • 已为慢数据配置流式传输

Reference Documentation

参考文档

  • Route Structure
  • Load Functions
  • 路由结构
  • Load Functions