sveltekit-server-actions
v0.1.0
Published
React 19-style server actions for SvelteKit
Maintainers
Readme
SvelteKit Server Actions
React 19-style server actions for SvelteKit. Write server-only functions that can be called directly from the client as if they were regular functions.
Features
- 🚀 Simple API - Just write async functions and call them from anywhere
- 🔒 Type-safe - Full TypeScript support with automatic type inference
- 🍪 Cookie Support - Access and modify cookies easily
- 🔄 Special Returns - Built-in support for redirects and page reloads
- 📦 Zero Config - Automatic discovery of
.server.tsfiles - ⚡ Vite Plugin - Seamless integration with SvelteKit
Installation
npm install sveltekit-server-actions
# or
pnpm add sveltekit-server-actionsSetup
1. Add the Vite plugin
// vite.config.ts
import { sveltekit } from '@sveltejs/kit/vite';
import { serverActions } from 'sveltekit-server-actions/vite';
import { defineConfig } from 'vite';
export default defineConfig({
plugins: [
sveltekit(),
serverActions({
// Optional: configure the actions endpoint
endpoint: '/api/actions' // default: '/__server-actions'
})
]
});2. Add the hooks
// src/hooks.server.ts
import { actionContext } from 'sveltekit-server-actions/server';
import type { Handle } from '@sveltejs/kit';
export const handle: Handle = async ({ event, resolve }) => {
// Create context for server actions
const context = {
cookies: event.cookies,
headers: event.request.headers,
locals: event.locals,
event
};
// Run the request with action context
return actionContext.run(context, async () => {
return await resolve(event);
});
};3. Create the actions endpoint
// src/routes/api/actions/+server.ts (or your configured endpoint)
import { json } from '@sveltejs/kit';
import { executeServerAction } from 'sveltekit-server-actions';
import type { RequestHandler } from './$types';
export const POST: RequestHandler = async ({ request, cookies, locals }) => {
const isActionRequest = request.headers.get('X-SvelteKit-Action') === 'true';
if (!isActionRequest) {
return new Response('Method not allowed', { status: 405 });
}
try {
const { actionId, args } = await request.json();
const context = {
cookies,
headers: request.headers,
locals,
event: { request, cookies, locals }
};
const result = await executeServerAction(actionId, args, context);
return json(result);
} catch (err) {
return json({
success: false,
error: {
message: err instanceof Error ? err.message : 'Unknown error',
code: 'ACTION_ERROR'
}
}, { status: 500 });
}
};4. Configure the client (optional)
// src/app.d.ts or in your app initialization
import { configureServerActions } from 'sveltekit-server-actions/client';
// If you're using a custom endpoint
configureServerActions({
endpoint: '/api/actions'
});Usage
Create server actions
// src/lib/actions/user.server.ts
import { getActionContext } from 'sveltekit-server-actions';
export async function updateProfile(name: string, email: string) {
const context = getActionContext();
const userId = context?.locals.userId;
// This runs only on the server
await db.users.update({
where: { id: userId },
data: { name, email }
});
return { success: true };
}
export async function logout() {
const context = getActionContext();
// Clear auth cookie
context?.cookies.delete('session', { path: '/' });
// Redirect to login page
const { redirect } = await import('sveltekit-server-actions');
return redirect('/login');
}Use from client components
<script lang="ts">
import { updateProfile, logout } from '$lib/actions/user.server';
let name = '';
let email = '';
async function handleSubmit() {
try {
await updateProfile(name, email);
alert('Profile updated!');
} catch (error) {
alert('Error: ' + error.message);
}
}
</script>
<form on:submit|preventDefault={handleSubmit}>
<input bind:value={name} placeholder="Name" />
<input bind:value={email} type="email" placeholder="Email" />
<button type="submit">Update Profile</button>
</form>
<button on:click={logout}>Logout</button>Special Returns
Redirects
import { redirect, redirectWithData } from 'sveltekit-server-actions';
export async function createPost(title: string) {
const post = await db.posts.create({ title });
// Simple redirect
return redirect(`/posts/${post.id}`);
// Or with data
return redirectWithData({ postId: post.id }, `/posts/${post.id}`);
}Page Reloads
import { reload, reloadWithData } from 'sveltekit-server-actions';
export async function changeTheme(theme: 'light' | 'dark') {
const context = getActionContext();
context?.cookies.set('theme', theme, { path: '/' });
// Reload the page to apply theme
return reload();
// Or with data
return reloadWithData({ theme });
}File Organization
Server action files must:
- End with
.server.tssuffix - Be placed anywhere in your
src/directory - Export async functions
Common patterns:
src/
├── lib/
│ └── actions/ # Centralized actions
│ ├── auth.server.ts
│ └── data.server.ts
└── routes/
└── dashboard/
└── actions.server.ts # Route-specific actionsTypeScript
Full type inference works automatically:
// Actions are fully typed
export async function getUser(id: number): Promise<User | null> {
return await db.users.findUnique({ where: { id } });
}
// Client usage has full type safety
const user = await getUser(123); // user is typed as User | nullLicense
MIT
