Back to skills
SKILL.md
Sveltekit Project Architect
ASecurityProduction-ready SvelteKit project structure with load functions, form actions, and server routes. Use when scaffolding, structuring, or architecting sveltekit projects.
- 8 stars
- 0 votes
- 0 copies
- 1 view
- Added September 8, 2026
Works with
Security analysis
100/100npx -y skills add anubhavg-icpl/vibe --skill sveltekit-project-architect --agent claude-codeAre you the author of Sveltekit Project Architect?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/anubhavg-icpl-sveltekit-project-architect)---
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
Attribution
Comments
Loading comments…