
Performance Optimizer
- 15 installs
- 4 repo stars
- Updated December 6, 2025
- ajianaz/skills-collection
performance-optimizer is a skill that optimizes SvelteKit apps by converting client-side fetching to server-side load functions and progressive enhancement.
About
performance-optimizer is a skill focused on optimizing SvelteKit applications by moving client-side data fetching to server-side load functions and form actions. A developer uses it when a page loads slowly with spinners, uses onMount plus fetch, or needs content in the initial HTML for SEO. It provides a 3-step conversion workflow and SvelteKit-specific patterns.
- Converts client-side fetching to SvelteKit server-side load functions
- Removes loading spinners for instant SSR and better SEO
- Progressive enhancement with form actions and type adapters
Performance Optimizer by the numbers
- 15 all-time installs (skills.sh)
- Ranked #1,609 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
performance-optimizer capabilities & compatibility
- Capabilities
- frontend · seo
- Works with
- supabase
- Use cases
- frontend · refactoring · seo
- Pricing
- Free
What performance-optimizer says it does
Optimize SvelteKit applications by leveraging SvelteKit's full-stack architecture for instant server-side rendering and progressive enhancement.
`onMount` + `fetch()` = slow initial load
npx skills add https://github.com/ajianaz/skills-collection --skill performance-optimizerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 15 |
|---|---|
| repo stars | ★ 4 |
| Last updated | December 6, 2025 |
| Repository | ajianaz/skills-collection ↗ |
What it does
Convert SvelteKit client-side fetching to server-side load functions for instant SSR and better SEO.
Who is it for?
Speeding up SvelteKit pages by replacing onMount+fetch with load functions and form actions.
Skip if: Non-SvelteKit frameworks or backend/database performance tuning.
When should I use this skill?
A SvelteKit page loads slowly with a spinner, uses onMount+fetch, or needs SSR content for SEO.
What you get
SvelteKit pages that render instantly server-side with progressive enhancement and SEO-friendly HTML.
- Server-side load functions
- Form actions
- Refactored components
By the numbers
- 3-step conversion workflow
Files
SvelteKit Performance Optimizer
Optimize SvelteKit applications by leveraging the framework's full-stack capabilities for instant server-side rendering and progressive enhancement.
Quick Diagnosis
Search for these anti-patterns in the codebase:
# Find client-side fetching patterns
rg -n "onMount.*fetch|\$state.*loading|writable\(\)" --type svelte
rg -n "fetch\(" src/routes/ --type svelte
rg -n "export let data" src/routes/ --type svelteRed flags:
onMount+fetch()= slow initial load$state(true)forisLoading= user sees spinnerwritable()orderivedfor initial page data = waterfall fetching- Missing
export let datain page components = not using load functions
3-Step Conversion Workflow
Step 1: Identify Data Requirements
Determine what data the page needs on initial render:
- Static/rarely-changing data → Universal Load Function (SSR + CSR)
- User-interactive data (filters, search) → Form Actions + Client-side Actions
- Real-time data → Server-Sent Events or WebSockets
Step 2: Extract Interactive Sections
Move sections with on:viewportenter, $state, on:click to separate components while preserving SvelteKit patterns:
<!-- src/lib/components/DataSection.svelte -->
<script lang="ts">
export let data: Item[]; // Receive data as props from load function
import { onMount } from 'svelte';
import { scrollReveal } from '$lib/actions/scrollReveal.js';
import { fly, fade } from 'svelte/transition';
let element: HTMLElement;
let isVisible = false;
// Client-side animation with Svelte patterns
onMount(() => {
scrollReveal(element);
});
</script>
<div
bind:this={element}
transition:fly={{ y: 20 }}
class:visible={isVisible}
>
{#each data as item}
<div transition:fade>
{item.content}
</div>
{/each}
</div>Step 3: Implement SvelteKit Load Functions
<!-- src/routes/+page.svelte -->
<script lang="ts">
import DataSection from '$lib/components/DataSection.svelte';
import type { PageData } from './$types';
export let data: PageData; // Data from universal load function
</script>
<DataSection {data} />
<!-- Optional: Progressive enhancement form -->
<form method="POST" action="?/submit">
<input name="message" />
<button type="submit">Submit</button>
</form>// src/routes/+page.server.ts
import { getData } from '$lib/server/data';
import type { PageServerLoad, Actions } from './$types';
import { fail } from '@sveltejs/kit';
export const load: PageServerLoad = async ({ url }) => {
const data = await getData(); // Fetch on server
return { data };
};
export const actions: Actions = {
default: async ({ request }) => {
const formData = await request.formData();
const message = formData.get('message');
if (!message) {
return fail(400, { message: 'Message is required' });
}
// Process data...
return { success: true };
}
};Type Adapter Pattern with SvelteKit
When DB types differ from frontend types:
// src/lib/server/adapters.ts
import type { Item as DBItem } from "$lib/server/database.types";
import type { Item } from "$lib/types";
export function adaptDBToFrontend(db: DBItem): Item {
return {
id: db.id,
name: db.name,
description: db.description ?? "",
createdAt: new Date(db.created_at),
};
}
// src/routes/+page.server.ts
import { getItems } from '$lib/server/items';
import { adaptDBToFrontend } from '$lib/server/adapters';
import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params, url, cookies }) => {
const dbItems = await getItems();
const items = dbItems.map(adaptDBToFrontend);
return {
items,
// Additional SvelteKit-specific data
search: url.searchParams.get('search') || '',
user: cookies.get('user') ? JSON.parse(cookies.get('user')) : null
};
};
// src/routes/+page.ts for universal load
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ parent, url }) => {
const parentData = await parent();
const clientData = await getClientOnlyData();
return {
...parentData,
clientData
};
};When to Use Hybrid Patterns
Keep client-side fetching when:
- Real-time subscriptions (Supabase realtime, WebSockets)
- User-triggered fetching (search, filters, pagination) - use form actions
- Data depends on client state (auth token, localStorage) - use universal load functions
- Infinite scroll / load more patterns - use load functions with pagination
Best practice: Use SvelteKit's progressive enhancement - server-side load + client-side enhancement
Advanced SvelteKit Patterns
See references/patterns.md for:
- Parallel data fetching with Promise.all in load functions
- Streaming with SvelteKit streaming responses and deferred loading
- Error handling with +error.svelte pages and form validation
- Caching strategies with cache headers and
depends() - Hybrid SSR + client patterns with form actions and progressive enhancement
- Route protection with
+layout.server.tshooks - Database transactions with form actions
- Real-time updates with server-sent events
RSC Data Fetching Patterns
Table of Contents
- Basic SSR Fetch
- Parallel Data Fetching
- Hybrid Pattern
- Streaming with Suspense
- Error Handling
- Caching Strategies
- Common Anti-Patterns
---
Basic SSR Fetch
Simple server-side data fetching:
// app/products/page.tsx
import { getProducts } from "@/lib/actions/products";
export default async function ProductsPage() {
const products = await getProducts();
return (
<div>
{products.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}---
Parallel Data Fetching
Fetch multiple data sources simultaneously:
// app/dashboard/page.tsx
export default async function DashboardPage() {
// ✅ Parallel fetching - both run at same time
const [users, orders, stats] = await Promise.all([
getUsers(),
getOrders(),
getStats(),
]);
return (
<div>
<UsersSection users={users} />
<OrdersSection orders={orders} />
<StatsSection stats={stats} />
</div>
);
}Avoid sequential fetching:
// ❌ Bad - waterfall, each waits for previous
const users = await getUsers();
const orders = await getOrders();
const stats = await getStats();---
Hybrid Pattern
Server-side initial data + client-side interactivity:
// app/products/page.tsx (Server Component)
import { getProducts, getCategories } from "@/lib/actions";
import { ProductsClient } from "./products-client";
export default async function ProductsPage() {
const [products, categories] = await Promise.all([
getProducts(),
getCategories(),
]);
return (
<ProductsClient
initialProducts={products}
categories={categories}
/>
);
}
// app/products/products-client.tsx (Client Component)
"use client";
import { useState } from "react";
interface ProductsClientProps {
initialProducts: Product[];
categories: Category[];
}
export function ProductsClient({ initialProducts, categories }: ProductsClientProps) {
const [products, setProducts] = useState(initialProducts);
const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
const handleFilter = async (categoryId: string) => {
setSelectedCategory(categoryId);
// Client-side fetch for filtering
const filtered = await filterProducts(categoryId);
setProducts(filtered);
};
return (
<div>
<CategoryFilter
categories={categories}
onFilter={handleFilter}
/>
<ProductGrid products={products} />
</div>
);
}---
Streaming with Suspense
Show content progressively as data loads:
// app/dashboard/page.tsx
import { Suspense } from "react";
export default function DashboardPage() {
return (
<div>
{/* Shows immediately */}
<h1>Dashboard</h1>
{/* Streams in when ready */}
<Suspense fallback={<StatsSkeleton />}>
<StatsSection />
</Suspense>
<Suspense fallback={<ChartSkeleton />}>
<ChartSection />
</Suspense>
</div>
);
}
// Async component - fetches its own data
async function StatsSection() {
const stats = await getStats(); // Can be slow
return <Stats data={stats} />;
}---
Error Handling
Handle fetch errors gracefully:
// app/products/page.tsx
import { getProducts } from "@/lib/actions/products";
export default async function ProductsPage() {
const products = await getProducts();
if (!products || products.length === 0) {
return <EmptyState message="No products found" />;
}
return <ProductList products={products} />;
}
// With error boundary (error.tsx)
// app/products/error.tsx
"use client";
export default function Error({
error,
reset,
}: {
error: Error;
reset: () => void;
}) {
return (
<div>
<h2>Something went wrong!</h2>
<button onClick={() => reset()}>Try again</button>
</div>
);
}---
Caching Strategies
Static Data (Build Time)
// Data fetched at build time, cached indefinitely
export const dynamic = "force-static";
export default async function Page() {
const data = await getData();
return <Content data={data} />;
}Revalidate on Interval
// Revalidate every 60 seconds
export const revalidate = 60;
export default async function Page() {
const data = await getData();
return <Content data={data} />;
}On-Demand Revalidation
// lib/actions/products.ts
"use server";
import { revalidatePath } from "next/cache";
export async function createProduct(data: ProductInput) {
await db.products.create(data);
revalidatePath("/products"); // Invalidate cache
}No Cache (Always Fresh)
export const dynamic = "force-dynamic";
export default async function Page() {
const data = await getData(); // Always fresh
return <Content data={data} />;
}---
Common Anti-Patterns
❌ Client-side fetch for static data
"use client";
export default function Page() {
const [data, setData] = useState([]);
useEffect(() => {
fetch("/api/data").then(r => r.json()).then(setData);
}, []);
return <List data={data} />;
}Fix: Remove "use client", use async function.
❌ Fetching in layout for page-specific data
// app/layout.tsx
export default async function Layout({ children }) {
const user = await getUser(); // Fetched on every page
return <div>{children}</div>;
}Fix: Fetch in page component or use React cache().
❌ Over-fetching with Context
// Fetches ALL data even if page only needs users
const { users, products, orders } = useStore();Fix: Fetch only what each page needs server-side.
❌ Ignoring parallel fetching
const a = await fetchA();
const b = await fetchB(); // Waits for A to completeFix: Use Promise.all([fetchA(), fetchB()]).
Related skills
FAQ
What does performance-optimizer fix?
Slow SvelteKit pages using onMount+fetch, by converting to server-side load functions and progressive enhancement.
Does it help SEO?
Yes, moving fetching server-side puts content in the initial HTML so search engines can read it.