
Rsc Data Optimizer
- 8 installs
- 142 repo stars
- Updated February 3, 2026
- julianromli/opencode-template
Helps with ai & agent building tasks.
About
rsc-data-optimizer is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- rsc-data-optimizer
- AI & Agent Building
- AI-coding skill
Rsc Data Optimizer by the numbers
- 8 all-time installs (skills.sh)
- Ranked #12,269 of 16,556 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/julianromli/opencode-template --skill rsc-data-optimizerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 8 |
|---|---|
| repo stars | ★ 142 |
| Last updated | February 3, 2026 |
| Repository | julianromli/opencode-template ↗ |
What it does
Helps with ai & agent building tasks.
Files
RSC Data Fetching Optimizer
Optimize slow client-side data fetching to instant server-side rendering.
Quick Diagnosis
Search for these anti-patterns in the codebase:
# Find client-side fetching patterns
rg -n "useEffect.*fetch|useState.*loading|useStore\(\)" --type tsx
rg -n '"use client"' app/ --type tsxRed flags:
"use client"+useEffect+fetch()= slow initial loaduseState(true)forisLoading= user sees spinneruseStore()oruseContextfor initial page data = waterfall fetching
3-Step Conversion Workflow
Step 1: Identify Data Requirements
Determine what data the page needs on initial render:
- Static/rarely-changing data → Server Component (SSR)
- User-interactive data (filters, search) → Client Component
Step 2: Extract Interactive Sections
Move sections with useInView, useState, onClick to separate Client Components:
// components/data-section.tsx
"use client";
interface DataSectionProps {
data: Item[]; // Receive data as props
}
export function DataSection({ data }: DataSectionProps) {
const [ref, inView] = useInView(); // Client-side animation OK
return <div ref={ref}>...</div>;
}Step 3: Convert Page to Server Component
// app/page.tsx - NO "use client"
import { getData } from "@/lib/actions/data";
import { DataSection } from "@/components/data-section";
export default async function Page() {
const data = await getData(); // Fetch on server
return <DataSection data={data} />;
}Type Adapter Pattern
When DB types differ from frontend types:
import type { Item as DBItem } from "@/lib/database.types";
import type { Item } from "@/lib/types";
function adaptDBToFrontend(db: DBItem): Item {
return {
id: db.id,
name: db.name,
description: db.description ?? "",
createdAt: new Date(db.created_at),
};
}
export default async function Page() {
const dbItems = await getItems();
const items = dbItems.map(adaptDBToFrontend);
return <ItemList items={items} />;
}When to Keep Client-Side
Keep "use client" when:
- Real-time subscriptions (Supabase realtime)
- User-triggered fetching (search, filters, pagination)
- Data depends on client state (auth token, localStorage)
- Infinite scroll / load more patterns
Advanced Patterns
See references/patterns.md for:
- Parallel data fetching
- Streaming with Suspense
- Error boundaries
- Caching strategies
- Hybrid SSR + client patterns
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()]).