← スキル一覧に戻る

react-best-practices
by majcheradam
react-best-practicesは、ソフトウェア開発を効率化するスキルです。開発ワークフロー全体をサポートし、チームの生産性向上とコード品質の改善を実現します。
⭐ 462🍴 34📅 2026年1月23日
SKILL.md
name: react-best-practices description: React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
React Best Practices
Overview
Comprehensive performance optimization guide for React and Next.js applications, containing 40+ rules across 8 categories. Rules are prioritized by impact to guide automated refactoring and code generation.
When to Apply
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
Priority-Ordered Guidelines
| Priority | Category | Impact |
|---|---|---|
| 1 | Eliminating Waterfalls | CRITICAL |
| 2 | Bundle Size Optimization | CRITICAL |
| 3 | Server-Side Performance | HIGH |
| 4 | Client-Side Data Fetching | MEDIUM-HIGH |
| 5 | Re-render Optimization | MEDIUM |
| 6 | Rendering Performance | MEDIUM |
| 7 | JavaScript Performance | LOW-MEDIUM |
| 8 | Advanced Patterns | LOW |
Category 1: Eliminating Waterfalls (CRITICAL)
Rule: Defer await until needed
Bad:
async function Page() {
const data = await fetchData(); // Blocks everything
return <Component data={data} />;
}
Good:
async function Page() {
const dataPromise = fetchData(); // Start immediately
return <Component dataPromise={dataPromise} />;
}
Rule: Use Promise.all() for independent operations
Bad:
const user = await getUser();
const posts = await getPosts();
const comments = await getComments();
Good:
const [user, posts, comments] = await Promise.all([
getUser(),
getPosts(),
getComments(),
]);
Rule: Start promises at the earliest point
Bad:
function Component() {
useEffect(() => {
fetchData().then(setData); // Too late
}, []);
}
Good:
// In route loader or parent component
const dataPromise = fetchData();
function Component({ dataPromise }) {
const data = use(dataPromise);
}
Rule: Use Suspense boundaries for streaming
<Suspense fallback={<Skeleton />}>
<AsyncComponent />
</Suspense>
Category 2: Bundle Size Optimization (CRITICAL)
Rule: Avoid barrel file imports
Bad:
import { Button } from "@/components"; // Pulls entire barrel
import { format } from "date-fns"; // Pulls entire library
Good:
import { Button } from "@/components/ui/button";
import { format } from "date-fns/format";
Rule: Use next/dynamic for heavy components
const HeavyChart = dynamic(() => import("./Chart"), {
loading: () => <ChartSkeleton />,
ssr: false,
});
Rule: Defer third-party libraries
// Load analytics only after interaction
const loadAnalytics = () => import("analytics").then((m) => m.init());
useEffect(() => {
window.addEventListener("click", loadAnalytics, { once: true });
}, []);
Rule: Configure optimizePackageImports
// next.config.js
module.exports = {
experimental: {
optimizePackageImports: ["lodash", "date-fns", "@radix-ui/react-icons"],
},
};
Category 3: Server-Side Performance (HIGH)
Rule: Use React.cache() for request deduplication
import { cache } from "react";
const getUser = cache(async (id: string) => {
return db.user.findUnique({ where: { id } });
});
Rule: Implement LRU caching for expensive computations
import { LRUCache } from "lru-cache";
const cache = new LRUCache<string, Data>({ max: 100 });
async function getData(key: string) {
if (cache.has(key)) return cache.get(key);
const data = await expensiveComputation(key);
cache.set(key, data);
return data;
}
Rule: Minimize serialization overhead
Bad:
// Passing entire objects when only ID needed
<ClientComponent user={user} />
Good:
<ClientComponent userId={user.id} />
Category 4: Client-Side Data Fetching (MEDIUM-HIGH)
Rule: Use SWR or React Query for client fetching
import useSWR from "swr";
function Profile({ userId }) {
const { data, error, isLoading } = useSWR(`/api/user/${userId}`, fetcher);
if (isLoading) return <Skeleton />;
if (error) return <Error />;
return <UserCard user={data} />;
}
Rule: Use lazy state initialization
Bad:
const [state, setState] = useState(expensiveComputation());
Good:
const [state, setState] = useState(() => expensiveComputation());
Rule: Use startTransition for non-urgent updates
import { startTransition } from "react";
function handleSearch(query) {
startTransition(() => {
setSearchResults(search(query));
});
}
Category 5: Re-render Optimization (MEDIUM)
Rule: Narrow state scope
Bad:
function Parent() {
const [count, setCount] = useState(0);
return (
<>
<Counter count={count} setCount={setCount} />
<ExpensiveList /> {/* Re-renders on every count change */}
</>
);
}
Good:
function Parent() {
return (
<>
<CounterWithState />
<ExpensiveList />
</>
);
}
Rule: Use useMemo for expensive calculations
const sortedItems = useMemo(
() => items.sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
Rule: Use useCallback for stable function references
const handleClick = useCallback((id) => {
setItems((items) => items.filter((item) => item.id !== id));
}, []);
Category 6: Rendering Performance (MEDIUM)
Rule: Use content-visibility for long lists
.list-item {
content-visibility: auto;
contain-intrinsic-size: 0 50px;
}
Rule: Wrap SVG animations to prevent layout recalculation
function AnimatedIcon() {
return (
<div style={{ contain: "layout" }}>
<AnimatedSVG />
</div>
);
}
Rule: Use conditional rendering over CSS hiding
Bad:
<div style={{ display: isVisible ? "block" : "none" }}>
<ExpensiveComponent />
</div>
Good:
{
isVisible && <ExpensiveComponent />;
}
Category 7: JavaScript Performance (LOW-MEDIUM)
Rule: Batch DOM updates
// Use refs for multiple rapid updates
const counterRef = useRef<HTMLSpanElement>(null);
function increment() {
if (counterRef.current) {
counterRef.current.textContent = String(++count);
}
}
Rule: Build index maps for repeated lookups
Bad:
items.forEach((item) => {
const related = otherItems.find((o) => o.id === item.relatedId);
});
Good:
const otherItemsById = new Map(otherItems.map((o) => [o.id, o]));
items.forEach((item) => {
const related = otherItemsById.get(item.relatedId);
});
Rule: Use immutable array methods
// Prefer these for predictable React updates
const updated = items.with(index, newValue);
const filtered = items.toSpliced(index, 1);
const sorted = items.toSorted((a, b) => a.name.localeCompare(b.name));
Category 8: Advanced Patterns (LOW)
Rule: Use useSyncExternalStore for external state
const value = useSyncExternalStore(
store.subscribe,
store.getSnapshot,
store.getServerSnapshot
);
Rule: Implement optimistic updates
const [optimisticItems, addOptimisticItem] = useOptimistic(
items,
(state, newItem) => [...state, { ...newItem, pending: true }]
);
Quick Reference Checklist
- No sequential awaits for independent data
- Using Promise.all() for parallel fetches
- No barrel file imports
- Heavy components use dynamic imports
- Suspense boundaries around async components
- State scoped to smallest necessary component
- useMemo/useCallback where beneficial
- content-visibility for long lists
スコア
総合スコア
85/100
リポジトリの品質指標に基づく評価
✓SKILL.md
SKILL.mdファイルが含まれている
+20
✓LICENSE
ライセンスが設定されている
+10
✓説明文
100文字以上の説明がある
+10
✓人気
GitHub Stars 100以上
+5
✓最近の活動
1ヶ月以内に更新
+10
✓フォーク
10回以上フォークされている
+5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
✓タグ
1つ以上のタグが設定されている
+5
レビュー
💬
レビュー機能は近日公開予定です


