From dev-infrastructure-skills
This skill should be used when the user asks to "write a React component", "optimize React performance", "fix React re-renders", "reduce bundle size", "eliminate waterfalls", "improve React patterns", "review React code", or needs guidance on React hooks, server components, data fetching, rendering optimization, or component architecture best practices.
How this skill is triggered — by the user, by Claude, or both
Slash command
/dev-infrastructure-skills:react-best-practicesThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Comprehensive React performance and architecture patterns organized by priority. Apply these rules when writing, reviewing, or optimizing React code.
Comprehensive React performance and architecture patterns organized by priority. Apply these rules when writing, reviewing, or optimizing React code.
Rules are organized into 8 categories by impact. Categories marked CRITICAL should always be checked.
Start async operations immediately but defer await until data is actually needed. This converts sequential fetches into parallel ones.
// BAD: Sequential — second fetch waits for first
async function Page() {
const user = await getUser();
const posts = await getPosts(user.id);
return <Feed user={user} posts={posts} />;
}
// GOOD: Parallel — both start immediately
async function Page() {
const userPromise = getUser();
const postsPromise = getPosts();
const user = await userPromise;
const posts = await postsPromise;
return <Feed user={user} posts={posts} />;
}
When fetches have dependencies, parallelize independent branches while respecting the dependency chain.
// GOOD: Parallel branches with dependencies
async function Dashboard() {
const [user, config] = await Promise.all([getUser(), getConfig()]);
const [posts, settings] = await Promise.all([
getPosts(user.id),
getUserSettings(user.id),
]);
return <DashboardView user={user} posts={posts} settings={settings} config={config} />;
}
Place <Suspense> boundaries to unlock parallel streaming. Each boundary creates an independent loading unit.
// GOOD: Independent loading for each section
function Page() {
return (
<div>
<Suspense fallback={<HeaderSkeleton />}>
<Header />
</Suspense>
<Suspense fallback={<FeedSkeleton />}>
<Feed />
</Suspense>
<Suspense fallback={<SidebarSkeleton />}>
<Sidebar />
</Suspense>
</div>
);
}
Import directly from source modules, not barrel files (index.ts). Barrel imports pull in entire libraries.
// BAD: Imports entire library
import { Button } from '@/components';
// GOOD: Direct import
import { Button } from '@/components/ui/button';
For third-party libraries, use optimizePackageImports in next.config:
experimental: {
optimizePackageImports: ['lucide-react', '@heroicons/react', 'date-fns']
}
Load heavy modules only when the runtime context requires them:
// GOOD: Load only needed parser
async function parseDocument(type: string) {
if (type === 'pdf') {
const { parsePDF } = await import('./parsers/pdf');
return parsePDF;
}
const { parseText } = await import('./parsers/text');
return parseText;
}
Use next/dynamic or lazy loading for below-the-fold or interaction-triggered components:
const HeavyChart = dynamic(() => import('./HeavyChart'), {
loading: () => <ChartSkeleton />,
ssr: false,
});
Keep state as close to where it's used as possible. When parent state changes, all children re-render.
useMemo and useCallback StrategicallyOnly memoize when: (a) expensive computation, (b) referential equality matters for child components, or (c) dependency of other hooks.
// GOOD: Expensive filter operation
const filtered = useMemo(
() => items.filter(complexPredicate).sort(expensiveSort),
[items]
);
// BAD: Simple value — memoization overhead exceeds benefit
const label = useMemo(() => `Hello ${name}`, [name]);
When context values change often, split into multiple contexts or use composition:
// GOOD: Children passed as props don't re-render on parent state change
function Layout({ children }: { children: React.ReactNode }) {
const [count, setCount] = useState(0);
return (
<div>
<Counter count={count} setCount={setCount} />
{children} {/* Does NOT re-render when count changes */}
</div>
);
}
Components are Server Components by default. Only add 'use client' when you need:
useState, useEffect, or other hookswindow, document)onClick, onChange)// GOOD: Server component — no client JS, no loading state needed
async function UserProfile({ id }: { id: string }) {
const user = await db.users.findUnique({ where: { id } });
return <ProfileCard user={user} />;
}
Server-to-client props must be JSON-serializable:
Date → use .toISOString()Map/Set → convert to Object.fromEntries() / Array.from()When writing or reviewing React code, verify:
'use client' only when neededuseMemo/useCallback used only for genuinely expensive operationsFor the complete 40+ rule reference with all code examples, see references/.
Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub moxywolfllc/moxywolf-plugins --plugin dev-infrastructure-skills