A powerful Vite plugin that automatically decorates SvelteKit functions with customizable wrappers for logging, analytics, error handling, and more.
Experience the plugin in action with a live, interactive demo. No installation required!
SvelteKit lacks a built-in way to execute common code across multiple functions (load functions, actions, API routes, remote functions). This plugin solves that by providing a decorator pattern that allows you to:
Install the plugin
npm install vite-plugin-sveltekit-decorators
Add to your Vite configuration
// vite.config.js
import { sveltekit } from '@sveltejs/kit/vite';
import { svelteKitDecorators } from 'vite-plugin-sveltekit-decorators';
export default {
plugins: [
sveltekit(),
svelteKitDecorators({
enabled: true,
debug: false
})
]
};
Create your decorator functions
// src/+decorators.server.ts
import type {
ServerLoadDecorator,
ActionsDecorator,
ApiDecorator,
RemoteFunctionDecorator
} from 'vite-plugin-sveltekit-decorators';
export const loadDecorator: ServerLoadDecorator = (originalFunction, metadata) => {
return async (event) => {
console.log(`Loading ${metadata.functionName}...`);
const result = await originalFunction(event);
console.log(`Loaded ${metadata.functionName} successfully`);
return result;
};
};
export const actionsDecorator: ActionsDecorator = (originalFunction, metadata) => {
return async (event) => {
console.log(`Executing action ${metadata.action}...`);
return await originalFunction(event);
};
};
export const apiDecorator: ApiDecorator = (originalFunction, metadata) => {
return async (event) => {
console.log(`API ${metadata.method} request to ${metadata.functionName}`);
return await originalFunction(event);
};
};
export const remoteFunctionDecorator: RemoteFunctionDecorator = (originalFunction, metadata) => {
return async (...args) => {
console.log(`Calling remote function ${metadata.functionName}...`);
const result = await originalFunction(...args);
console.log(`Remote function ${metadata.functionName} completed`);
return result;
};
};
That's it! Your existing SvelteKit code will automatically be decorated without any modifications.
Check out our complete working example which demonstrates all features including:
| Decorator | Purpose | File Location | Applies To |
|---|---|---|---|
loadDecorator |
Client-side load functions | src/+decorators.ts |
Page/layout load functions |
loadDecorator |
Server-side load functions | src/+decorators.server.ts |
Page/layout load functions on server |
actionsDecorator |
Form actions | src/+decorators.server.ts |
Page actions (default, named) |
apiDecorator |
API route handlers | src/+decorators.server.ts |
API routes (GET, POST, etc.) |
remoteFunctionDecorator |
Remote functions (RPC) | src/+decorators.server.ts |
Functions in .remote.ts file |
| Option | Type | Default | Description |
|---|---|---|---|
enabled |
boolean | 'dev' | 'build' |
true |
Enable/disable the plugin |
debug |
boolean |
false |
Enable debug logging |
serverWrapperFile |
string |
'./src/+decorators.server.ts' |
Path to server decorators file |
clientWrapperFile |
string |
'./src/+decorators.ts' |
Path to client decorators file |
You can configure decorators per page using the decorators configuration, following SvelteKit's convention:
// +page.server.ts - Just like other SvelteKit config exports!
export const config = {
decorators: {
load: true, // Enable load decoration
actions: ['create'], // Only decorate 'create' action
api: false // Disable API decoration
}
};
export const load = async () => { /* automatically decorated */ };
export const actions = {
create: async () => { /* decorated */ },
update: async () => { /* decorated */ },
delete: async () => { /* NOT decorated */ }
};
Configuration options:
true: Enable for all functionsfalse: Disable completely string[]: Enable only for specific actions/HTTP methodsThis follows the exact same pattern as SvelteKit's built-in configuration - no new concepts to learn!
Note: Remote functions (
.remote.tsfiles) do not currently support granular configuration viaexport const configdue to SvelteKit restrictions. Decorators are applied to all remote functions ifremoteFunctionDecoratoris defined.
This plugin is fully type-safe and provides:
The plugin works by transforming your code at build time. You don't need to:
Your existing code remains untouched and clean.
This plugin follows SvelteKit's established patterns for a familiar developer experience:
+ file conventions (+decorators.ts, +decorators.server.ts)export const config, just like SvelteKit's own patterns// Debugging works exactly as you'd expect
export const loadDecorator: ServerLoadDecorator = (originalFunction, metadata) => {
return async (event) => {
debugger; // ā
Works perfectly!
console.log(`Loading ${metadata.functionName}...`);
const result = await originalFunction(event);
return result;
};
};
This plugin has zero performance impact on your application because:
ā ļø Performance depends on your decorator implementation - avoid heavy computations or blocking operations in your decorators.
Each decorator receives metadata about the function:
interface BaseDecoratorMetadata {
functionName: string; // Name of the original function
isAsync: boolean; // Whether the function is async
}
// Server decorators also include:
interface ServerDecoratorMetadata extends BaseDecoratorMetadata {
filePath: string; // File path (server-side only)
startLine: number; // Line number where function starts
endLine: number; // Line number where function ends
}
// Action decorators include:
interface ActionsDecoratorMetadata extends ServerDecoratorMetadata {
functionType: 'actions';
action: string; // Action name ('default' for default action)
}
// API decorators include:
interface ApiDecoratorMetadata extends ServerDecoratorMetadata {
functionType: 'api';
method: string; // HTTP method (GET, POST, etc.)
}
export const loadDecorator: ServerLoadDecorator = (originalFunction, metadata) => {
return async (event) => {
// Only log in development
if (import.meta.env.DEV) {
console.log(`Loading ${metadata.functionName}...`);
}
const start = Date.now();
const result = await originalFunction(event);
// Performance monitoring
const duration = Date.now() - start;
if (duration > 1000) {
console.warn(`Slow load function: ${metadata.functionName} (${duration}ms)`);
}
return result;
};
};
Decorate SvelteKit's remote functions in .remote.ts files:
// src/lib/api.remote.ts
import z from 'zod';
import { prerender } from '$app/server';
export const getUser = prerender(async () => {
// This function will be automatically decorated
return { name: 'John Doe' };
});
// With validation
export const updateProfile = prerender(
z.string(), // validation function
async (name) => { // remote function (decorated)
return { name: name };
}
);
The remoteFunctionDecorator wraps the inner function:
export const remoteFunctionDecorator: RemoteFunctionDecorator = (originalFunction, metadata) => {
return async (...args) => {
console.log(`š Remote function called: ${metadata.functionName}`);
console.log(`š¦ Arguments:`, args);
const result = await originalFunction(...args);
console.log(`ā
Result:`, result);
return result;
};
};
export const apiDecorator: ApiDecorator = (originalFunction, metadata) => {
return async (event) => {
try {
return await originalFunction(event);
} catch (error) {
console.error(`API error in ${metadata.functionName}:`, error);
return new Response(
JSON.stringify({ error: 'Internal server error' }),
{
status: 500,
headers: { 'Content-Type': 'application/json' }
}
);
}
};
};
MIT License - see LICENSE file for details.
Contributions are welcome! Please feel free to submit issues and pull requests.