sveltekit
1
总安装量
1
周安装量
#41491
全站排名
安装命令
npx skills add https://github.com/lcaparros/sveltekit-skills --skill sveltekit
Agent 安装分布
opencode
1
Skill 文档
SvelteKit Frontend Development Skill
Overview
Expert guidance for building modern, performant web applications with SvelteKit.
When to Use This Skill
- Creating new SvelteKit applications
- Building user interfaces and components
- Implementing client-server communication with Eden Treaty
- Setting up routing and layouts
- Managing state and forms
Stack Context
- Framework: SvelteKit
- Runtime: Bun (dev server)
- API Client: Eden Treaty (type-safe ElysiaJS client)
- Code Style: Standard JS (no semicolons)
- Build: Vite (bundled with SvelteKit)
Project Structure
packages/web/
âââ src/
â âââ routes/
â â âââ +page.svelte # Home page
â â âââ +layout.svelte # Root layout
â â âââ users/
â â â âââ +page.svelte # Users list
â â â âââ [id]/
â â â âââ +page.svelte # User detail
â â âââ api/
â â âââ client.js # API client
â âââ lib/
â â âââ components/
â â â âââ Header.svelte
â â â âââ Button.svelte
â â âââ stores/
â â â âââ user.js
â â âââ utils/
â â âââ format.js
â âââ app.html # HTML template
âââ static/
â âââ favicon.png
âââ package.json
âââ svelte.config.js
âââ vite.config.js
Workflows
1. Initialize SvelteKit Project
# Create package
mkdir -p packages/web
cd packages/web
# Initialize SvelteKit
bun create svelte@latest .
# Select options:
# - Skeleton project
# - TypeScript: No (using JS)
# - Add ESLint: Yes
# - Add Prettier: Yes
# Install dependencies
bun install
# Add Eden Treaty for API client
bun add @elysiajs/eden
package.json:
{
"name": "@monorepo/web",
"version": "0.1.0",
"type": "module",
"scripts": {
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"lint": "standard"
},
"dependencies": {
"@elysiajs/eden": "^1.0.0"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"svelte": "^4.0.0",
"vite": "^5.0.0"
}
}
2. Basic SvelteKit Configuration
svelte.config.js:
import adapter from '@sveltejs/adapter-auto'
export default {
kit: {
adapter: adapter()
}
}
vite.config.js:
import { sveltekit } from '@sveltejs/kit/vite'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [sveltekit()],
server: {
port: 5173
}
})
3. API Client Setup with Eden Treaty
src/lib/api/client.js:
import { edenTreaty } from '@elysiajs/eden'
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3000'
export const api = edenTreaty(API_URL)
// Helper functions
export async function getUsers () {
const { data, error } = await api.users.get()
if (error) throw new Error(error.message || 'Failed to fetch users')
return data.users
}
export async function getUser (id) {
const { data, error } = await api.users[id].get()
if (error) throw new Error(error.message || 'Failed to fetch user')
return data.user
}
export async function createUser (userData) {
const { data, error } = await api.users.post(userData)
if (error) throw new Error(error.message || 'Failed to create user')
return data
}
export async function updateUser (id, userData) {
const { data, error } = await api.users[id].put(userData)
if (error) throw new Error(error.message || 'Failed to update user')
return data
}
export async function deleteUser (id) {
const { data, error } = await api.users[id].delete()
if (error) throw new Error(error.message || 'Failed to delete user')
return data
}
.env:
VITE_API_URL=http://localhost:3000
4. Root Layout
src/routes/+layout.svelte:
<script>
import '../app.css'
</script>
<div class="app">
<header>
<nav>
<a href="/">Home</a>
<a href="/users">Users</a>
<a href="/posts">Posts</a>
</nav>
</header>
<main>
<slot />
</main>
<footer>
<p>© 2024 My App</p>
</footer>
</div>
<style>
.app {
min-height: 100vh;
display: flex;
flex-direction: column;
}
header {
background: #333;
color: white;
padding: 1rem;
}
nav {
display: flex;
gap: 1rem;
}
nav a {
color: white;
text-decoration: none;
}
nav a:hover {
text-decoration: underline;
}
main {
flex: 1;
padding: 2rem;
max-width: 1200px;
margin: 0 auto;
width: 100%;
}
footer {
background: #f5f5f5;
padding: 1rem;
text-align: center;
}
</style>
5. Home Page
src/routes/+page.svelte:
<script>
let name = 'World'
</script>
<svelte:head>
<title>Home</title>
</svelte:head>
<div class="container">
<h1>Welcome to SvelteKit</h1>
<p>Hello {name}!</p>
<input bind:value={name} placeholder="Enter your name" />
</div>
<style>
.container {
text-align: center;
}
h1 {
font-size: 3rem;
margin-bottom: 1rem;
}
input {
padding: 0.5rem;
font-size: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
margin-top: 1rem;
}
</style>
6. Page with Server Load
src/routes/users/+page.js:
import { getUsers } from '$lib/api/client'
export async function load () {
try {
const users = await getUsers()
return { users }
} catch (error) {
return {
users: [],
error: error.message
}
}
}
src/routes/users/+page.svelte:
<script>
export let data
</script>
<svelte:head>
<title>Users</title>
</svelte:head>
<div class="container">
<h1>Users</h1>
{#if data.error}
<p class="error">{data.error}</p>
{:else if data.users.length === 0}
<p>No users found.</p>
{:else}
<ul class="users-list">
{#each data.users as user}
<li>
<a href="/users/{user._id}">
<h3>{user.name}</h3>
<p>{user.email}</p>
</a>
</li>
{/each}
</ul>
{/if}
<a href="/users/new" class="button">Create User</a>
</div>
<style>
.container {
max-width: 800px;
}
.users-list {
list-style: none;
padding: 0;
display: grid;
gap: 1rem;
}
.users-list li {
background: white;
border: 1px solid #ddd;
border-radius: 8px;
padding: 1rem;
transition: box-shadow 0.2s;
}
.users-list li:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.users-list a {
text-decoration: none;
color: inherit;
}
.users-list h3 {
margin: 0 0 0.5rem 0;
}
.users-list p {
margin: 0;
color: #666;
}
.button {
display: inline-block;
margin-top: 2rem;
padding: 0.75rem 1.5rem;
background: #0066cc;
color: white;
text-decoration: none;
border-radius: 4px;
}
.button:hover {
background: #0052a3;
}
.error {
color: red;
padding: 1rem;
background: #ffe6e6;
border-radius: 4px;
}
</style>
7. Dynamic Route
src/routes/users/[id]/+page.js:
import { getUser } from '$lib/api/client'
import { error } from '@sveltejs/kit'
export async function load ({ params }) {
try {
const user = await getUser(params.id)
return { user }
} catch (err) {
throw error(404, 'User not found')
}
}
src/routes/users/[id]/+page.svelte:
<script>
export let data
</script>
<svelte:head>
<title>{data.user.name}</title>
</svelte:head>
<div class="container">
<h1>{data.user.name}</h1>
<p><strong>Email:</strong> {data.user.email}</p>
<p><strong>Created:</strong> {new Date(data.user.createdAt).toLocaleDateString()}</p>
<div class="actions">
<a href="/users/{data.user._id}/edit" class="button">Edit</a>
<a href="/users" class="button secondary">Back to Users</a>
</div>
</div>
<style>
.container {
max-width: 600px;
}
.actions {
display: flex;
gap: 1rem;
margin-top: 2rem;
}
.button {
padding: 0.75rem 1.5rem;
background: #0066cc;
color: white;
text-decoration: none;
border-radius: 4px;
}
.button:hover {
background: #0052a3;
}
.button.secondary {
background: #666;
}
.button.secondary:hover {
background: #555;
}
</style>
8. Form with Actions
src/routes/users/new/+page.server.js:
import { createUser } from '$lib/api/client'
import { redirect } from '@sveltejs/kit'
export const actions = {
default: async ({ request }) => {
const formData = await request.formData()
const name = formData.get('name')
const email = formData.get('email')
try {
await createUser({ name, email })
throw redirect(303, '/users')
} catch (error) {
return {
error: error.message,
name,
email
}
}
}
}
src/routes/users/new/+page.svelte:
<script>
import { enhance } from '$app/forms'
export let form
</script>
<svelte:head>
<title>Create User</title>
</svelte:head>
<div class="container">
<h1>Create User</h1>
{#if form?.error}
<p class="error">{form.error}</p>
{/if}
<form method="POST" use:enhance>
<div class="field">
<label for="name">Name</label>
<input
id="name"
name="name"
type="text"
required
value={form?.name || ''}
/>
</div>
<div class="field">
<label for="email">Email</label>
<input
id="email"
name="email"
type="email"
required
value={form?.email || ''}
/>
</div>
<div class="actions">
<button type="submit">Create User</button>
<a href="/users" class="button secondary">Cancel</a>
</div>
</form>
</div>
<style>
.container {
max-width: 600px;
}
.field {
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: bold;
}
input {
width: 100%;
padding: 0.75rem;
font-size: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
}
.actions {
display: flex;
gap: 1rem;
margin-top: 2rem;
}
button {
padding: 0.75rem 1.5rem;
background: #0066cc;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
}
button:hover {
background: #0052a3;
}
.button.secondary {
background: #666;
text-decoration: none;
padding: 0.75rem 1.5rem;
color: white;
border-radius: 4px;
}
.error {
color: red;
padding: 1rem;
background: #ffe6e6;
border-radius: 4px;
margin-bottom: 1rem;
}
</style>
9. Reusable Components
src/lib/components/Button.svelte:
<script>
export let variant = 'primary'
export let type = 'button'
export let href = null
</script>
{#if href}
<a {href} class="button {variant}">
<slot />
</a>
{:else}
<button {type} class="button {variant}" on:click>
<slot />
</button>
{/if}
<style>
.button {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
text-decoration: none;
display: inline-block;
}
.button.primary {
background: #0066cc;
color: white;
}
.button.primary:hover {
background: #0052a3;
}
.button.secondary {
background: #666;
color: white;
}
.button.secondary:hover {
background: #555;
}
.button.danger {
background: #cc0000;
color: white;
}
.button.danger:hover {
background: #a30000;
}
</style>
Usage:
<script>
import Button from '$lib/components/Button.svelte'
</script>
<Button>Default Button</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="danger">Delete</Button>
<Button href="/users">Link Button</Button>
10. Stores for State Management
src/lib/stores/user.js:
import { writable } from 'svelte/store'
export const currentUser = writable(null)
export function setUser (user) {
currentUser.set(user)
}
export function clearUser () {
currentUser.set(null)
}
Usage:
<script>
import { currentUser } from '$lib/stores/user'
</script>
{#if $currentUser}
<p>Welcome, {$currentUser.name}!</p>
{:else}
<p>Please log in</p>
{/if}
Best Practices
-
Routing:
- Use file-based routing
- Implement layouts for shared UI
- Use
+page.jsfor data loading - Use
+page.server.jsfor server-only code
-
Data Loading:
- Load data in
loadfunctions - Handle errors gracefully
- Show loading states
- Load data in
-
Forms:
- Use SvelteKit form actions
- Use
enhancefor progressive enhancement - Validate on client and server
-
Components:
- Keep components small and focused
- Use props for configuration
- Use slots for composition
- Colocate styles with components
-
API Communication:
- Use Eden Treaty for type-safe API calls
- Centralize API client logic
- Handle errors consistently
-
Performance:
- Use SSR by default
- Preload data for better UX
- Lazy load heavy components
- Optimize images
Common Patterns
Loading state:
<script>
import { onMount } from 'svelte'
let loading = true
let data = []
onMount(async () => {
try {
data = await fetchData()
} finally {
loading = false
}
})
</script>
{#if loading}
<p>Loading...</p>
{:else}
<!-- Show data -->
{/if}
Conditional rendering:
{#if condition}
<p>True</p>
{:else if otherCondition}
<p>Other</p>
{:else}
<p>False</p>
{/if}
Lists:
{#each items as item (item.id)}
<div>{item.name}</div>
{/each}