{data.post.title}
\nBy {data.post.author.name}
\n \n {@html data.post.content}\n \n {#if data.canEdit}\n \n {/if}\nBy {data.post.author.name}
\n \n {@html data.post.content}\n \n {#if data.canEdit}\n \n {/if}\nLoading related posts...
\n {:then posts}\nFailed to load related posts
\n {/await}\nFull-stack with SvelteKit
# SvelteKit Advanced Patterns
You are an expert in SvelteKit for building full-stack web applications with SSR, API routes, and advanced data loading patterns.
## Key Principles
- Use +page.server.ts for secure data loading
- Implement form actions for mutations
- Leverage streaming with defer/await
- Add middleware with hooks
- Deploy with appropriate adapters
## Project Structure
```
src/
├── routes/
│ ├── +layout.svelte # Root layout
│ ├── +layout.server.ts # Root server layout load
│ ├── +page.svelte # Home page
│ ├── +page.server.ts # Home server load
│ ├── +error.svelte # Error boundary
│ ├── api/
│ │ └── users/
│ │ └── +server.ts # API endpoint
│ ├── dashboard/
│ │ ├── +layout.svelte # Dashboard layout
│ │ ├── +layout.server.ts # Auth guard
│ │ ├── +page.svelte
│ │ └── settings/
│ │ ├── +page.svelte
│ │ └── +page.server.ts
│ └── blog/
│ ├── +page.svelte # Blog list
│ └── [slug]/
│ ├── +page.svelte # Blog post
│ └── +page.server.ts
├── lib/
│ ├── server/ # Server-only code
│ │ └── db.ts
│ └── components/
├── hooks.server.ts # Server hooks
└── app.d.ts # Type definitions
```
## Server Load Functions
```typescript
// src/routes/blog/[slug]/+page.server.ts
import { error } from "@sveltejs/kit";
import type { PageServerLoad, Actions } from "./$types";
import { db } from "$lib/server/db";
export const load: PageServerLoad = async ({ params, locals, depends }) => {
// Add dependency for invalidation
depends("app:blog");
const post = await db.post.findUnique({
where: { slug: params.slug },
include: { author: true, comments: { take: 10 } }
});
if (!post) {
error(404, { message: "Post not found" });
}
// Check permissions
const canEdit = locals.user?.id === post.authorId;
return {
post,
canEdit,
// Streamed data (loads in parallel, renders when ready)
streamed: {
relatedPosts: db.post.findMany({
where: {
category: post.category,
id: { not: post.id }
},
take: 5
})
}
};
};
// Form actions
export const actions: Actions = {
comment: async ({ request, locals, params }) => {
if (!locals.user) {
error(401, "Must be logged in");
}
const data = await request.formData();
const content = data.get("content") as string;
if (!content || content.length < 3) {
return fail(400, {
error: "Comment must be at least 3 characters",
content
});
}
await db.comment.create({
data: {
content,
postSlug: params.slug,
authorId: locals.user.id
}
});
return { success: true };
},
delete: async ({ locals, params }) => {
const post = await db.post.findUnique({ where: { slug: params.slug } });
if (post?.authorId !== locals.user?.id) {
error(403, "Not authorized");
}
await db.post.delete({ where: { slug: params.slug } });
redirect(303, "/blog");
}
};
```
## Page Component with Forms
```svelte
<!-- src/routes/blog/[slug]/+page.svelte -->
<script lang="ts">
import { enhance } from "$app/forms";
import { invalidate } from "$app/navigation";
import type { PageData, ActionData } from "./$types";
let { data, form }: { data: PageData, form: ActionData } = $props();
let submitting = $state(false);
</script>
<article>
<h1>{data.post.title}</h1>
<p>By {data.post.author.name}</p>
{@html data.post.content}
{#if data.canEdit}
<form method="POST" action="?/delete" use:enhance>
<button type="submit">Delete Post</button>
</form>
{/if}
</article>
<section>
<h2>Comments</h2>
<form
method="POST"
action="?/comment"
use:enhance={() => {
submitting = true;
return async ({ update, result }) => {
submitting = false;
if (result.type === "success") {
await invalidate("app:blog");
}
await update();
};
}}
>
<textarea name="content" required minlength="3">{form?.content ?? ""}</textarea>
{#if form?.error}
<p class="error">{form.error}</p>
{/if}
<button type="submit" disabled={submitting}>
{submitting ? "Posting..." : "Post Comment"}
</button>
</form>
{#each data.post.comments as comment}
<div class="comment">
<p>{comment.content}</p>
<small>{comment.author.name}</small>
</div>
{/each}
</section>
<!-- Streamed content -->
<section>
<h2>Related Posts</h2>
{#await data.streamed.relatedPosts}
<p>Loading related posts...</p>
{:then posts}
<ul>
{#each posts as post}
<li><a href="/blog/{post.slug}">{post.title}</a></li>
{/each}
</ul>
{:catch}
<p>Failed to load related posts</p>
{/await}
</section>
```
## Server Hooks
```typescript
// src/hooks.server.ts
import type { Handle, HandleServerError } from "@sveltejs/kit";
import { sequence } from "@sveltejs/kit/hooks";
import { db } from "$lib/server/db";
const auth: Handle = async ({ event, resolve }) => {
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;
event.locals.session = session;
}
}
return resolve(event);
};
const logger: Handle = async ({ event, resolve }) => {
const start = performance.now();
const response = await resolve(event);
const duration = performance.now() - start;
console.log(`${event.request.method} ${event.url.pathname} - ${response.status} (${duration.toFixed(2)}ms)`);
return response;
};
const security: Handle = async ({ event, resolve }) => {
const response = await resolve(event);
response.headers.set("X-Frame-Options", "DENY");
response.headers.set("X-Content-Type-Options", "nosniff");
response.headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
return response;
};
export const handle = sequence(logger, auth, security);
export const handleError: HandleServerError = async ({ error, event }) => {
console.error("Server error:", error);
return {
message: "An unexpected error occurred",
code: "INTERNAL_ERROR"
};
};
```
## API Endpoints
```typescript
// src/routes/api/users/+server.ts
import { json, error } from "@sveltejs/kit";
import type { RequestHandler } from "./$types";
export const GET: RequestHandler = async ({ url, locals }) => {
const limit = Number(url.searchParams.get("limit")) || 10;
const offset = Number(url.searchParams.get("offset")) || 0;
const users = await db.user.findMany({
take: limit,
skip: offset,
select: { id: true, name: true, email: true }
});
return json({ users, limit, offset });
};
export const POST: RequestHandler = async ({ request, locals }) => {
if (!locals.user?.isAdmin) {
error(403, "Admin access required");
}
const { name, email } = await request.json();
const user = await db.user.create({
data: { name, email }
});
return json(user, { status: 201 });
};
```
## Best Practices
- Use +page.server.ts for data that needs auth
- Prefer form actions over API routes for mutations
- Implement proper error boundaries
- Use streaming for slow data fetches
- Add CSRF protection with SvelteKit defaults
- Deploy with adapter-auto for platform detectionThis SvelteKit prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their sveltekit implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For SvelteKit projects, consider mentioning your framework version, coding style, and any specific libraries you're using.
{comment.content}
\n {comment.author.name}\n