Production-ready SvelteKit project structure with load functions, form actions, and server routes. Use when scaffolding, structuring, or architecting sveltekit projects.
Scanned 9/8/2026
Install to Claude Code
npx -y skills add anubhavg-icpl/vibe --skill sveltekit-project-architect --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Sveltekit Project Architect?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-sveltekit-project-architect)More formats (shields.io, HTML) on the badges page.
---
name: sveltekit-project-architect
description: Production-ready SvelteKit project structure with load functions, form actions, and server routes. Use when scaffolding, structuring, or architecting sveltekit projects.
license: CC-BY-NC-SA-4.0
metadata:
risk: unknown
source: community
kind: mode
category: project-structure
tags: [sveltekit, svelte, typescript, fullstack, project-structure]
---
# SvelteKit Project Architect Mode
You are an expert in structuring production-ready SvelteKit applications with file-based routing, load functions, and form actions.
## Project Structure
```text
sveltekit-project/
├── src/
│ ├── app.html # HTML template
│ ├── app.d.ts # Type declarations
│ ├── hooks.server.ts # Server hooks
│ ├── hooks.client.ts # Client hooks
│ │
│ ├── routes/
│ │ ├── +page.svelte # Home page
│ │ ├── +page.server.ts # Home loader
│ │ ├── +layout.svelte # Root layout
│ │ ├── +layout.server.ts # Root layout loader
│ │ ├── +error.svelte # Error page
│ │ │
│ │ ├── (auth)/ # Auth group
│ │ │ ├── +layout.svelte
│ │ │ ├── login/
│ │ │ │ ├── +page.svelte
│ │ │ │ └── +page.server.ts
│ │ │ └── register/
│ │ │ ├── +page.svelte
│ │ │ └── +page.server.ts
│ │ │
│ │ ├── (app)/ # App group (protected)
│ │ │ ├── +layout.svelte
│ │ │ ├── +layout.server.ts
│ │ │ ├── dashboard/
│ │ │ │ └── +page.svelte
│ │ │ └── users/
│ │ │ ├── +page.svelte
│ │ │ ├── +page.server.ts
│ │ │ ├── [id]/
│ │ │ │ ├── +page.svelte
│ │ │ │ ├── +page.server.ts
│ │ │ │ └── edit/
│ │ │ │ ├── +page.svelte
│ │ │ │ └── +page.server.ts
│ │ │ └── new/
│ │ │ ├── +page.svelte
│ │ │ └── +page.server.ts
│ │ │
│ │ └── api/
│ │ ├── users/
│ │ │ ├── +server.ts
│ │ │ └── [id]/
│ │ │ └── +server.ts
│ │ └── auth/
│ │ └── +server.ts
│ │
│ ├── lib/
│ │ ├── index.ts # $lib exports
│ │ ├── server/
│ │ │ ├── db.ts # Database client
│ │ │ ├── auth.ts # Auth utilities
│ │ │ └── email.ts # Email service
│ │ ├── components/
│ │ │ ├── ui/
│ │ │ │ ├── Button.svelte
│ │ │ │ ├── Input.svelte
│ │ │ │ ├── Card.svelte
│ │ │ │ └── Modal.svelte
│ │ │ ├── forms/
│ │ │ │ ├── UserForm.svelte
│ │ │ │ └── LoginForm.svelte
│ │ │ └── layout/
│ │ │ ├── Header.svelte
│ │ │ ├── Sidebar.svelte
│ │ │ └── Footer.svelte
│ │ ├── stores/
│ │ │ ├── user.ts
│ │ │ └── theme.ts
│ │ └── utils/
│ │ ├── validation.ts
│ │ └── format.ts
│ │
│ └── params/
│ └── id.ts # Param matcher
│
├── static/
│ ├── favicon.png
│ └── images/
│
├── prisma/
│ ├── schema.prisma
│ └── migrations/
│
├── tests/
│ ├── unit/
│ └── e2e/
│
├── .env.example
├── package.json
├── svelte.config.js
├── tsconfig.json
├── vite.config.ts
└── README.md
```
## Core Files
```svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts">
import { page } from '$app/stores';
import Header from '$lib/components/layout/Header.svelte';
import '../app.css';
export let data;
</script>
<svelte:head>
<title>{$page.data.title || 'SvelteKit App'}</title>
</svelte:head>
<div class="min-h-screen bg-gray-50">
<Header user={data.user} />
<main class="container mx-auto py-8">
<slot />
</main>
</div>
```
```typescript
// src/routes/+layout.server.ts
import type { LayoutServerLoad } from "./$types";
import { getUser } from "$lib/server/auth";
export const load: LayoutServerLoad = async ({ cookies }) => {
const sessionId = cookies.get("session");
const user = sessionId ? await getUser(sessionId) : null;
return { user };
};
```
```svelte
<!-- src/routes/(app)/users/+page.svelte -->
<script lang="ts">
import { enhance } from '$app/forms';
import { invalidateAll } from '$app/navigation';
import Button from '$lib/components/ui/Button.svelte';
export let data;
export let form;
let deleting: string | null = null;
</script>
<div class="space-y-6">
<div class="flex justify-between items-center">
<h1 class="text-2xl font-bold">Users ({data.total})</h1>
<a href="/users/new">
<Button>Add User</Button>
</a>
</div>
{#if form?.error}
<div class="bg-red-100 text-red-700 p-4 rounded">
{form.error}
</div>
{/if}
<div class="bg-white rounded-lg shadow overflow-hidden">
<table class="min-w-full">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-sm font-medium">Name</th>
<th class="px-6 py-3 text-left text-sm font-medium">Email</th>
<th class="px-6 py-3 text-left text-sm font-medium">Role</th>
<th class="px-6 py-3 text-right text-sm font-medium">Actions</th>
</tr>
</thead>
<tbody class="divide-y">
{#each data.users as user (user.id)}
<tr>
<td class="px-6 py-4">
<a href="/users/{user.id}" class="hover:underline">
{user.name}
</a>
</td>
<td class="px-6 py-4">{user.email}</td>
<td class="px-6 py-4">
<span class="px-2 py-1 text-xs rounded-full bg-gray-100">
{user.role}
</span>
</td>
<td class="px-6 py-4 text-right space-x-2">
<a href="/users/{user.id}/edit" class="text-blue-600 hover:underline">
Edit
</a>
<form
method="POST"
action="?/delete"
use:enhance={() => {
deleting = user.id;
return async ({ update }) => {
await update();
deleting = null;
};
}}
class="inline"
>
<input type="hidden" name="id" value={user.id} />
<button
type="submit"
disabled={deleting === user.id}
class="text-red-600 hover:underline disabled:opacity-50"
>
{deleting === user.id ? 'Deleting...' : 'Delete'}
</button>
</form>
</td>
</tr>
{/each}
</tbody>
</table>
</div>
<!-- Pagination -->
{#if data.totalPages > 1}
<div class="flex justify-center gap-2">
{#each Array(data.totalPages) as _, i}
<a
href="?page={i + 1}"
class="px-3 py-1 rounded {data.page === i + 1 ? 'bg-blue-600 text-white' : 'bg-gray-200'}"
>
{i + 1}
</a>
{/each}
</div>
{/if}
</div>
```
```typescript
// src/routes/(app)/users/+page.server.ts
import type { PageServerLoad, Actions } from "./$types";
import { fail, redirect } from "@sveltejs/kit";
import { db } from "$lib/server/db";
import { z } from "zod";
const PER_PAGE = 10;
export const load: PageServerLoad = async ({ url }) => {
const page = parseInt(url.searchParams.get("page") || "1");
const search = url.searchParams.get("q") || "";
const where = search
? {
OR: [{ name: { contains: search, mode: "insensitive" } }, { email: { contains: search, mode: "insensitive" } }],
}
: {};
const [users, total] = await Promise.all([
db.user.findMany({
where,
skip: (page - 1) * PER_PAGE,
take: PER_PAGE,
orderBy: { createdAt: "desc" },
}),
db.user.count({ where }),
]);
return {
users,
total,
page,
totalPages: Math.ceil(total / PER_PAGE),
};
};
export const actions: Actions = {
delete: async ({ request }) => {
const formData = await request.formData();
const id = formData.get("id") as string;
try {
await db.user.delete({ where: { id } });
return { success: true };
} catch (error) {
return fail(500, { error: "Failed to delete user" });
}
},
create: async ({ request }) => {
const formData = await request.formData();
const data = Object.fromEntries(formData);
const schema = z.object({
name: z.string().min(2),
email: z.string().email(),
role: z.enum(["user", "admin"]),
});
const result = schema.safeParse(data);
if (!result.success) {
return fail(400, {
errors: result.error.flatten().fieldErrors,
data,
});
}
const user = await db.user.create({ data: result.data });
throw redirect(303, `/users/${user.id}`);
},
};
```
```typescript
// src/hooks.server.ts
import type { Handle } from "@sveltejs/kit";
import { db } from "$lib/server/db";
export const handle: Handle = async ({ event, resolve }) => {
// Get session from cookie
const sessionId = event.cookies.get("session");
if (sessionId) {
const session = await db.session.findUnique({
where: { id: sessionId },
include: { user: true },
});
if (session && session.expiresAt > new Date()) {
event.locals.user = session.user;
} else {
// Clear expired session
event.cookies.delete("session", { path: "/" });
}
}
// Protected routes
if (event.url.pathname.startsWith("/dashboard") || event.url.pathname.startsWith("/users")) {
if (!event.locals.user) {
return new Response(null, {
status: 303,
headers: { location: "/login" },
});
}
}
return resolve(event);
};
```
```typescript
// src/routes/api/users/+server.ts
import type { RequestHandler } from "./$types";
import { json } from "@sveltejs/kit";
import { db } from "$lib/server/db";
export const GET: RequestHandler = async ({ url }) => {
const page = parseInt(url.searchParams.get("page") || "1");
const limit = parseInt(url.searchParams.get("limit") || "10");
const users = await db.user.findMany({
skip: (page - 1) * limit,
take: limit,
select: {
id: true,
name: true,
email: true,
role: true,
createdAt: true,
},
});
return json({ users });
};
export const POST: RequestHandler = async ({ request }) => {
const data = await request.json();
const user = await db.user.create({
data,
select: {
id: true,
name: true,
email: true,
role: true,
},
});
return json(user, { status: 201 });
};
```
```javascript
// svelte.config.js
import adapter from "@sveltejs/adapter-node";
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: vitePreprocess(),
kit: {
adapter: adapter(),
alias: {
$components: "src/lib/components",
},
},
};
export default config;
```
## Best Practices
- Use load functions for data fetching
- Use form actions for mutations
- Group routes with (parentheses)
- Use +page.server.ts for server-only code
- Implement hooks for auth/middleware
- Use $lib for shared code
- Validate with Zod on server
- Use enhance for progressive enhancement
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!