From claude-code-toolkit
Provides React Server Components patterns, streaming SSR, code splitting, bundle optimization, and Core Web Vitals guidance for Next.js apps.
npx claudepluginhub rohitg00/awesome-claude-code-toolkitThis skill uses the workspace's default tool permissions.
Server Components run on the server and send rendered HTML to the client. They can directly access databases, filesystems, and internal APIs without exposing them to the browser.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Guides MCP server integration in Claude Code plugins via .mcp.json or plugin.json configs for stdio, SSE, HTTP types, enabling external services as tools.
Server Components run on the server and send rendered HTML to the client. They can directly access databases, filesystems, and internal APIs without exposing them to the browser.
// app/products/page.tsx (Server Component by default)
async function ProductsPage() {
const products = await db.query("SELECT * FROM products WHERE active = true");
return (
<main>
<h1>Products</h1>
<ProductList products={products} />
<AddToCartButton /> {/* Client Component */}
</main>
);
}
Rules:
useState, useEffect, or browser APIs'use client' at the top of the file'use client' boundary as deep in the tree as possibleimport { Suspense } from 'react';
export default function Dashboard() {
return (
<div>
<Header /> {/* renders immediately */}
<Suspense fallback={<ChartSkeleton />}>
<AnalyticsChart /> {/* streams when ready */}
</Suspense>
<Suspense fallback={<TableSkeleton />}>
<RecentOrders /> {/* streams independently */}
</Suspense>
</div>
);
}
Each Suspense boundary streams independently. Place boundaries around data-fetching components to avoid blocking the entire page.
import dynamic from 'next/dynamic';
const HeavyEditor = dynamic(() => import('@/components/Editor'), {
loading: () => <EditorSkeleton />,
ssr: false,
});
const AdminPanel = dynamic(() => import('@/components/AdminPanel'));
Split on:
// next.config.js
module.exports = {
experimental: {
optimizePackageImports: ['lucide-react', '@heroicons/react', 'lodash-es'],
},
};
Checklist:
npx next build and review the output size per route@next/bundle-analyzer to identify large dependenciesmoment with date-fns or dayjs (save ~200KB)import { debounce } from 'lodash-es/debounce'import { Search } from 'lucide-react'| Metric | Good | Needs Work | Poor |
|---|---|---|---|
| LCP (Largest Contentful Paint) | <2.5s | 2.5-4.0s | >4.0s |
| INP (Interaction to Next Paint) | <200ms | 200-500ms | >500ms |
| CLS (Cumulative Layout Shift) | <0.1 | 0.1-0.25 | >0.25 |
<link rel="preload" as="image" href="..." />priority prop on above-the-fold <Image> componentswidth/height on images to prevent layout shiftsimport Image from 'next/image';
<Image
src="/hero.jpg"
alt="Descriptive alt text"
width={1200}
height={630}
priority // preload for LCP images
sizes="(max-width: 768px) 100vw, 50vw"
placeholder="blur"
blurDataURL={base64} // inline tiny placeholder
/>
next/image or equivalent (automatic WebP/AVIF, responsive srcset)sizes attribute to avoid downloading oversized imagesplaceholder="blur" with a base64 data URL for perceived performance// app/layout.tsx
import { Inter } from 'next/font/google';
const inter = Inter({
subsets: ['latin'],
display: 'swap', // show fallback font immediately
preload: true,
variable: '--font-inter',
});
export default function RootLayout({ children }) {
return (
<html className={inter.variable}>
<body>{children}</body>
</html>
);
}
next/font for zero-CLS font loading with automatic subsettingdisplay: 'swap' to avoid invisible text during loadwidth and height on images and videosaspect-ratio CSS for responsive media containersmin-heightcontain: layout for components that change sizeimport { onCLS, onINP, onLCP } from 'web-vitals';
onCLS(console.log);
onINP(console.log);
onLCP(console.log);
Measure real user metrics (RUM), not just lab scores. Vercel Analytics and Google Search Console provide field data.