Back to list
5dlabs

react-best-practices

by 5dlabs

Cognitive Task Orchestrator - GitOps on Bare Metal or Cloud for AI Agents

2🍴 1📅 Jan 25, 2026

SKILL.md


name: react-best-practices description: React and Next.js performance optimization guidelines. Use when writing, reviewing, or refactoring React/Next.js code. Applies to Blaze (web), Spark (desktop), and Tap (mobile) agents.

React Best Practices

Comprehensive performance optimization guide for React and Next.js applications. Contains 45+ rules across 8 categories, prioritized by impact.

When to Use

Reference these guidelines when:

  • Writing new React components or Next.js pages
  • Implementing data fetching (client or server-side)
  • Reviewing code for performance issues
  • Refactoring existing React/Next.js code
  • Optimizing bundle size or load times

Relevant Agents: Blaze (React/Next.js), Spark (Electron), Tap (Expo/React Native)


Rule Categories by Priority

PriorityCategoryImpact
1Eliminating WaterfallsCRITICAL
2Bundle Size OptimizationCRITICAL
3Server-Side PerformanceHIGH
4Client-Side Data FetchingMEDIUM-HIGH
5Re-render OptimizationMEDIUM
6Rendering PerformanceMEDIUM
7JavaScript PerformanceLOW-MEDIUM

1. Eliminating Waterfalls (CRITICAL)

Waterfalls are the #1 performance killer. Fix these first.

Move await into branches

Bad:

async function handleRequest(userId: string, skipProcessing: boolean) {
  const userData = await fetchUserData(userId);  // Always waits

  if (skipProcessing) {
    return { skipped: true };  // Waited for nothing
  }

  return processUserData(userData);
}

Good:

async function handleRequest(userId: string, skipProcessing: boolean) {
  if (skipProcessing) {
    return { skipped: true };  // Returns immediately
  }

  const userData = await fetchUserData(userId);
  return processUserData(userData);
}

Parallelize independent operations

Bad:

const user = await fetchUser(id);
const posts = await fetchPosts(id);
const comments = await fetchComments(id);
// Total time: user + posts + comments

Good:

const [user, posts, comments] = await Promise.all([
  fetchUser(id),
  fetchPosts(id),
  fetchComments(id),
]);
// Total time: max(user, posts, comments)

Start promises early, await late

Bad:

export async function GET() {
  const data = await fetchData();  // Blocks immediately
  const transformed = transform(data);
  return Response.json(transformed);
}

Good:

export async function GET() {
  const dataPromise = fetchData();  // Start immediately
  // ... do other setup work ...
  const data = await dataPromise;   // Await when needed
  return Response.json(transform(data));
}

Use Suspense boundaries for streaming

<Suspense fallback={<Loading />}>
  <SlowComponent />
</Suspense>

2. Bundle Size Optimization (CRITICAL)

Every KB matters for initial load.

Import directly, avoid barrel files

Bad:

import { Button } from '@/components';  // Pulls entire barrel

Good:

import { Button } from '@/components/Button';  // Only Button

Use dynamic imports for heavy components

Bad:

import { HeavyChart } from './HeavyChart';

function Dashboard() {
  return showChart ? <HeavyChart /> : null;
}

Good:

import dynamic from 'next/dynamic';

const HeavyChart = dynamic(() => import('./HeavyChart'), {
  loading: () => <ChartSkeleton />,
});

function Dashboard() {
  return showChart ? <HeavyChart /> : null;
}

Defer third-party scripts

Bad:

import { Analytics } from '@analytics/lib';

function App() {
  useEffect(() => {
    Analytics.init();  // Blocks hydration
  }, []);
}

Good:

function App() {
  useEffect(() => {
    // Load after hydration
    import('@analytics/lib').then(({ Analytics }) => {
      Analytics.init();
    });
  }, []);
}

Preload on hover/focus

function Link({ href, children }) {
  const preload = () => {
    const link = document.createElement('link');
    link.rel = 'prefetch';
    link.href = href;
    document.head.appendChild(link);
  };

  return (
    <a href={href} onMouseEnter={preload} onFocus={preload}>
      {children}
    </a>
  );
}

3. Server-Side Performance (HIGH)

Use React.cache() for per-request deduplication

import { cache } from 'react';

const getUser = cache(async (id: string) => {
  return await db.user.findUnique({ where: { id } });
});

// Multiple components can call getUser(id) - only one DB query

Minimize data passed to client components

Bad:

// Server Component
async function UserPage({ id }) {
  const user = await getFullUser(id);  // 50 fields
  return <ClientProfile user={user} />;
}

Good:

// Server Component
async function UserPage({ id }) {
  const user = await getFullUser(id);
  return (
    <ClientProfile
      name={user.name}
      avatar={user.avatar}
      // Only what client needs
    />
  );
}

Use after() for non-blocking operations

import { after } from 'next/server';

export async function POST(request: Request) {
  const data = await request.json();
  const result = await saveToDb(data);

  after(async () => {
    await sendAnalytics(result);
    await notifyWebhooks(result);
  });

  return Response.json(result);  // Returns immediately
}

4. Client-Side Data Fetching (MEDIUM-HIGH)

Use SWR for automatic deduplication

import useSWR from 'swr';

function useUser(id: string) {
  return useSWR(`/api/users/${id}`, fetcher, {
    dedupingInterval: 2000,  // Dedup requests within 2s
  });
}

// Multiple components using useUser(same-id) = one request

Deduplicate global event listeners

Bad:

function Component() {
  useEffect(() => {
    window.addEventListener('resize', handler);  // Each instance adds one
    return () => window.removeEventListener('resize', handler);
  }, []);
}

Good:

// Shared hook with ref counting
const listeners = new Set();

function useWindowResize(handler: () => void) {
  useEffect(() => {
    listeners.add(handler);
    if (listeners.size === 1) {
      window.addEventListener('resize', notifyAll);
    }
    return () => {
      listeners.delete(handler);
      if (listeners.size === 0) {
        window.removeEventListener('resize', notifyAll);
      }
    };
  }, [handler]);
}

5. Re-render Optimization (MEDIUM)

Don't subscribe to state only used in callbacks

Bad:

function Form() {
  const [value, setValue] = useState('');  // Re-renders on every keystroke

  const handleSubmit = () => {
    submitForm(value);
  };

  return <input onChange={e => setValue(e.target.value)} />;
}

Good:

function Form() {
  const valueRef = useRef('');

  const handleSubmit = () => {
    submitForm(valueRef.current);
  };

  return <input onChange={e => { valueRef.current = e.target.value }} />;
}

Extract expensive work into memoized components

Bad:

function Parent({ data, filter }) {
  return (
    <div>
      <ExpensiveList data={data} />  {/* Re-renders when filter changes */}
      <Filter value={filter} />
    </div>
  );
}

Good:

const MemoizedList = memo(ExpensiveList);

function Parent({ data, filter }) {
  return (
    <div>
      <MemoizedList data={data} />  {/* Only re-renders when data changes */}
      <Filter value={filter} />
    </div>
  );
}

Use functional setState for stable callbacks

Bad:

const increment = useCallback(() => {
  setCount(count + 1);  // Dependency on count
}, [count]);

Good:

const increment = useCallback(() => {
  setCount(c => c + 1);  // No dependencies
}, []);

Use startTransition for non-urgent updates

import { startTransition } from 'react';

function SearchBox() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);

  const handleChange = (e) => {
    setQuery(e.target.value);  // Urgent: update input

    startTransition(() => {
      setResults(search(e.target.value));  // Non-urgent: can be interrupted
    });
  };
}

6. Rendering Performance (MEDIUM)

Animate wrapper divs, not SVG elements

Bad:

<motion.svg animate={{ scale: 1.2 }}>  {/* Triggers SVG recalc */}
  <path d="..." />
</motion.svg>

Good:

<motion.div animate={{ scale: 1.2 }}>  {/* GPU accelerated */}
  <svg><path d="..." /></svg>
</motion.div>

Use content-visibility for long lists

.list-item {
  content-visibility: auto;
  contain-intrinsic-size: 0 50px;
}

Extract static JSX outside components

Bad:

function Component() {
  return (
    <div>
      <header>Static Header</header>  {/* Recreated every render */}
      <DynamicContent />
    </div>
  );
}

Good:

const StaticHeader = <header>Static Header</header>;

function Component() {
  return (
    <div>
      {StaticHeader}  {/* Same reference */}
      <DynamicContent />
    </div>
  );
}

Use ternary, not && for conditionals

Bad:

{items.length && <List items={items} />}  // Renders "0" when empty

Good:

{items.length > 0 ? <List items={items} /> : null}

7. JavaScript Performance (LOW-MEDIUM)

Build Map for repeated lookups

Bad:

users.forEach(user => {
  const role = roles.find(r => r.userId === user.id);  // O(n) each time
});

Good:

const roleMap = new Map(roles.map(r => [r.userId, r]));
users.forEach(user => {
  const role = roleMap.get(user.id);  // O(1)
});

Combine multiple iterations

Bad:

const active = users.filter(u => u.active);
const names = active.map(u => u.name);
const sorted = names.sort();
// 3 iterations

Good:

const names = [];
for (const u of users) {
  if (u.active) names.push(u.name);
}
names.sort();
// 1 iteration + sort

Check length before expensive operations

Bad:

if (items.some(item => expensiveCheck(item))) { ... }

Good:

if (items.length > 0 && items.some(item => expensiveCheck(item))) { ... }

Use Set/Map for O(1) lookups

Bad:

const isSelected = selectedIds.includes(id);  // O(n)

Good:

const selectedSet = new Set(selectedIds);
const isSelected = selectedSet.has(id);  // O(1)

Quick Reference Checklist

Before submitting React/Next.js code:

Critical (Fix First)

  • No sequential awaits for independent operations
  • Dynamic imports for components >50KB
  • No barrel file imports in hot paths
  • Third-party scripts loaded after hydration

High Priority

  • Server components minimize client-passed data
  • React.cache() for repeated data fetches
  • Proper Suspense boundaries

Medium Priority

  • Memoized expensive components
  • Stable callback references (functional setState)
  • No unnecessary re-renders from state subscriptions

  • test-driven-development - TDD for React components
  • verification-before-completion - Verify bundle size, performance metrics

Score

Total Score

65/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

1ヶ月以内に更新

+10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

+5

Reviews

💬

Reviews coming soon