agentsclimarketplace

React best practices

Skill Inteligentsensingsolutions/tdd-dev-workflow/skills/react-best-practices

This skill should be used when the user asks about "React performance", "React components", "Server Components", "Client Components", "re-renders", "React hooks", "bundle size", "code splitting", "lazy loading", "error boundaries", "suspense", "React optimization", or needs guidance on React best practices and performance.From its SKILL.md

Install
npx -y skills add Inteligentsensingsolutions/tdd-dev-workflow --skill react-best-practices

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

10.8 KB, as published. Nobody here has run it

React Best Practices

Performance optimization guidelines adapted from Vercel Engineering's 45 rules across 8 categories.


1. Eliminating Waterfalls

Sequential data fetching creates cascading delays. Eliminate waterfalls at every layer.

Rules

  • Fetch independent data in parallel with Promise.all or Promise.allSettled.
  • Use Server Components for data fetching to move requests to the server edge.
  • Avoid client-side fetch cascades where component A fetches, renders component B, which then fetches.
  • Prefetch linked pages with next/link (automatic in Next.js App Router) or manual router.prefetch().
  • Colocate data requirements with the component that uses them. Do not hoist fetches to parent components unnecessarily.
  • Use loading.tsx or <Suspense> boundaries to stream partial content while slow requests complete.

Pattern: Parallel Data Fetching

// Server Component
async function Dashboard() {
  const [user, stats, notifications] = await Promise.all([
    getUser(),
    getStats(),
    getNotifications(),
  ]);

  return (
    <div>
      <UserProfile user={user} />
      <StatsPanel stats={stats} />
      <NotificationList notifications={notifications} />
    </div>
  );
}

Anti-Pattern: Sequential Fetching

// Each component fetches only after parent renders
function Page() {
  const { data: user } = useSWR('/api/user');
  // Notifications wait for user to render
  return user ? <Notifications userId={user.id} /> : null;
}

2. Bundle Size

Every kilobyte of JavaScript sent to the client costs performance. Minimize aggressively.

Rules

  • Use next/dynamic or React.lazy for heavy components not needed on initial render.
  • Tree-shake imports: import { specific } from 'lib' instead of import lib from 'lib'.
  • Analyze bundle composition with @next/bundle-analyzer.
  • Avoid importing entire libraries when only a single utility is needed.
  • Prefer native browser APIs over library equivalents when support is sufficient.
  • Mark client-only packages in next.config.js with serverExternalPackages to avoid bundling them server-side.

Pattern: Dynamic Import

import dynamic from 'next/dynamic';

const HeavyChart = dynamic(() => import('@/components/Chart'), {
  loading: () => <ChartSkeleton />,
  ssr: false,
});

Pattern: Targeted Imports

// Good: tree-shakeable
import { format, parseISO } from 'date-fns';

// Bad: imports entire library
import * as dateFns from 'date-fns';

3. Server-Side Performance

Server Components are the default rendering strategy. Push client boundaries to the leaves.

Rules

  • Default to Server Components. Only add 'use client' when the component needs browser APIs, event handlers, or React hooks with state.
  • Move 'use client' directives to the smallest leaf components possible.
  • Use streaming with <Suspense> to progressively render content as data becomes available.
  • Cache expensive computations and data fetches with unstable_cache or React.cache.
  • Avoid passing serialization-heavy objects across the server/client boundary.
  • Use generateStaticParams for pages that can be statically generated.

Pattern: Leaf Client Component

// layout.tsx - Server Component (no 'use client')
export default function Layout({ children }) {
  return (
    <main>
      <ServerNav />       {/* Server: static nav */}
      {children}
      <InteractiveFooter /> {/* Client: only this needs interactivity */}
    </main>
  );
}

4. Client-Side Data Fetching

When client-side data fetching is necessary, use mature libraries with built-in caching.

Rules

  • Use TanStack Query or SWR for client-side data fetching. Do not build custom fetch hooks.
  • Implement stale-while-revalidate to show cached data immediately while refreshing in the background.
  • Deduplicate requests: both TanStack Query and SWR deduplicate identical requests automatically.
  • Set appropriate staleTime and gcTime (garbage collection time) to avoid redundant network requests.
  • Prefetch data for routes the user is likely to navigate to.
  • Invalidate caches explicitly after mutations.

Pattern: TanStack Query

const { data, isLoading } = useQuery({
  queryKey: ['user', userId],
  queryFn: () => fetchUser(userId),
  staleTime: 5 * 60 * 1000, // 5 minutes
});

Pattern: Mutation with Invalidation

const mutation = useMutation({
  mutationFn: updateUser,
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['user'] });
  },
});

5. Re-render Optimization

Unnecessary re-renders are the most common source of sluggish React UIs. Measure before optimizing.

Rules

  • Use React.memo only when React DevTools Profiler shows a component re-rendering with unchanged props.
  • Lift state up minimally. Only the closest common ancestor that needs the state should hold it.
  • Apply useMemo for expensive computations that recalculate on every render.
  • Apply useCallback to stabilize function references passed as props to memoized children.
  • Use individual Zustand selectors: useStore(s => s.count) instead of useStore().
  • Avoid deriving state in render. Compute derived values in useMemo or in the store.

Pattern: Selective Zustand Subscription

// Good: only re-renders when count changes
const count = useStore((s) => s.count);

// Bad: re-renders on any store change
const { count } = useStore();

Pattern: Stable Callback

const handleClick = useCallback(
  (id: string) => dispatch({ type: 'SELECT', id }),
  [dispatch]
);

return <MemoizedList items={items} onSelect={handleClick} />;

6. Rendering Performance

Optimize how React renders large datasets, frequent updates, and complex DOM structures.

Rules

  • Virtualize long lists with react-window or @tanstack/react-virtual. Never render 1000+ DOM nodes.
  • Debounce rapid state updates (search input, resize handlers) with useDeferredValue or a debounce utility.
  • Avoid inline object/array creation in JSX props. Create them outside the render or memoize them.
  • Use CSS for animations instead of React state changes when possible.
  • Split large components into smaller pieces with their own <Suspense> boundaries.
  • Prefer key prop with stable unique IDs, never array indices for dynamic lists.

Pattern: Virtualized List

import { FixedSizeList } from 'react-window';

function VirtualList({ items }: { items: Item[] }) {
  return (
    <FixedSizeList height={600} itemCount={items.length} itemSize={50} width="100%">
      {({ index, style }) => (
        <div style={style}>{items[index].name}</div>
      )}
    </FixedSizeList>
  );
}

Anti-Pattern: Inline Objects

// Bad: creates new object every render, breaks memoization
<Component style={{ color: 'red' }} config={{ threshold: 10 }} />

// Good: stable references
const style = useMemo(() => ({ color: 'red' }), []);
const config = useMemo(() => ({ threshold: 10 }), []);
<Component style={style} config={config} />

7. JavaScript Performance

Optimize the JavaScript runtime for computational efficiency.

Rules

  • Prefer Map and Set over plain objects for frequent lookups, additions, and deletions.
  • Avoid unnecessary array copies. Use mutating methods inside reducers or controlled scopes.
  • Use Web Workers for CPU-heavy tasks (image processing, data transformation, encryption).
  • Debounce event handlers for scroll, resize, and mousemove events.
  • Use structuredClone over JSON.parse(JSON.stringify(...)) for deep cloning.
  • Prefer for...of or Array.prototype methods over manual index loops for readability and safety.

Pattern: Web Worker for Heavy Computation

// worker.ts
self.onmessage = (e) => {
  const result = heavyComputation(e.data);
  self.postMessage(result);
};

// component.tsx
const worker = useMemo(() => new Worker(new URL('./worker.ts', import.meta.url)), []);

8. Anti-Patterns

Recognize and avoid these common mistakes that degrade React application performance.

Anti-PatternProblemSolution
'use client' everywhereShips all components as client JavaScriptDefault to Server Components, push 'use client' to leaves
Fetching in useEffect when server fetch worksCreates waterfalls, shows loading spinnersFetch in Server Components or use loader
Missing loading/error statesBroken UI during network delays or failuresAlways provide <Suspense> fallback and error boundaries
Prop drilling through many layersFragile component coupling, unnecessary re-rendersUse Zustand store or React Context at the right level
State for derived valuesRedundant state, sync bugsCompute in useMemo or derive in render
Giant monolithic componentsHard to test, slow to re-renderExtract into focused, composable components
Ignoring key prop warningsIncorrect reconciliation, lost stateUse stable unique IDs as keys
Premature optimizationCode complexity without measured benefitProfile first with React DevTools, then optimize

Quick Reference

CategoryKey RuleImpact
WaterfallsParallel fetches with Promise.allReduces load time
Bundle SizeDynamic imports for heavy componentsSmaller initial JS
Server-SideDefault to Server ComponentsLess client JS
Client DataTanStack Query / SWRCache + dedup
Re-rendersIndividual Zustand selectorsFewer re-renders
RenderingVirtualize long listsLess DOM work
JS PerfMap/Set for lookupsFaster operations
Anti-PatternsProfile before optimizingAvoid wasted effort

References

  • For the complete 45-rule catalog organized by category, read references/rules-overview.md.
  • For detailed individual rules with incorrect/correct code examples, browse references/rules/. Each file covers one rule with impact rating, tags, and working code. Section prefixes: async- (waterfalls), bundle- (bundle size), server- (SSR), client- (data fetching), rerender- (re-renders), rendering- (DOM), js- (JS perf), advanced- (patterns).

Dependencies

SkillReason
typescript-proReact components require strong TypeScript typing for props, generics, and discriminated unions

Used By

SkillReason
nextjs-patternsNext.js builds on React; Server/Client Component rules and rendering strategies apply directly
zustand-patternsZustand store subscriptions and selector patterns depend on React re-render optimization rules

Keep looking

Skills are one crate of 628,971. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.