
React Nextjs Patterns
- 461 installs
- 10 repo stars
- Updated July 24, 2026
- duyet/claude-plugins
react-nextjs-patterns is a frontend skill that provides React and Next.js implementation patterns for performance and maintainability for developers building components, pages, and App Router applications in the React ec
About
This skill supplies React and Next.js patterns for building performant, maintainable frontend components and pages. A developer uses it when implementing App Router components, state management, or server/client component decisions.
- Next.js App Router server vs client component patterns
- State management and performance optimization guidance
React Nextjs Patterns by the numbers
- 461 all-time installs (skills.sh)
- Ranked #635 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/duyet/claude-plugins --skill react-nextjs-patternsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 461 |
|---|---|
| repo stars | ★ 10 |
| Last updated | July 24, 2026 |
| Repository | duyet/claude-plugins ↗ |
What React and Next.js patterns improve frontend performance?
Provides React and Next.js implementation patterns for server/client components, state management, and performance.
Who is it for?
Frontend developers implementing React components and Next.js App Router pages who want consistent performance and maintainability patterns.
Skip if: Backend-only engineers or teams not using React or the Next.js App Router ecosystem.
When should I use this skill?
A developer asks to implement React components, Next.js pages, Server versus Client Components, or frontend performance patterns.
What you get
React component implementations, Next.js App Router page structures, and performance-oriented frontend patterns
- React component code patterns
- Next.js page and route structures
Files
This skill provides React and Next.js specific patterns for building performant, maintainable frontend applications.
When to Invoke This Skill
Automatically activate for:
- React component implementation
- Next.js page and API routes
- State management patterns
- Performance optimization
- Server/Client component decisions
Next.js App Router Patterns
Server vs Client Components
// Server Component (default) - data fetching, no interactivity
// app/users/page.tsx
export default async function UsersPage() {
const users = await getUsers(); // Runs on server
return (
<div>
<h1>Users</h1>
<UserList users={users} />
</div>
);
}
// Client Component - interactivity required
// components/user-search.tsx
'use client';
import { useState } from 'react';
export function UserSearch({ onSearch }: { onSearch: (q: string) => void }) {
const [query, setQuery] = useState('');
return (
<input
value={query}
onChange={(e) => setQuery(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && onSearch(query)}
/>
);
}Streaming with Suspense
// app/dashboard/page.tsx
import { Suspense } from 'react';
export default function DashboardPage() {
return (
<div>
<h1>Dashboard</h1>
{/* Fast data loads first */}
<Suspense fallback={<StatsSkeleton />}>
<StatsSection />
</Suspense>
{/* Slow data streams in */}
<Suspense fallback={<ChartSkeleton />}>
<AnalyticsChart />
</Suspense>
</div>
);
}
// Async component that streams
async function StatsSection() {
const stats = await getStats(); // Can be slow
return <Stats data={stats} />;
}Data Fetching Patterns
// Parallel data fetching
async function DashboardPage() {
// Fetch in parallel, not sequentially
const [users, orders, stats] = await Promise.all([
getUsers(),
getOrders(),
getStats(),
]);
return <Dashboard users={users} orders={orders} stats={stats} />;
}
// With error boundary
import { notFound } from 'next/navigation';
async function UserPage({ params }: { params: { id: string } }) {
const user = await getUser(params.id);
if (!user) {
notFound(); // Renders not-found.tsx
}
return <UserProfile user={user} />;
}React Performance Patterns
Component Decomposition
// BAD: Large component with all state
function BadUserList() {
const [filter, setFilter] = useState('');
const [users, setUsers] = useState<User[]>([]);
const [selectedId, setSelectedId] = useState<string | null>(null);
// All users re-render on any state change
return (
<div>
<input value={filter} onChange={e => setFilter(e.target.value)} />
{users.map(user => (
<div
key={user.id}
onClick={() => setSelectedId(user.id)}
className={selectedId === user.id ? 'selected' : ''}
>
{user.name}
</div>
))}
</div>
);
}
// GOOD: Push state down to where it's needed
function GoodUserList() {
const [users] = useState<User[]>([]);
return <FilterableUserList users={users} />;
}
function FilterableUserList({ users }: { users: User[] }) {
const [filter, setFilter] = useState('');
const filtered = useMemo(
() => users.filter(u => u.name.includes(filter)),
[users, filter]
);
return (
<div>
<input value={filter} onChange={e => setFilter(e.target.value)} />
<SelectableList users={filtered} />
</div>
);
}
function SelectableList({ users }: { users: User[] }) {
const [selectedId, setSelectedId] = useState<string | null>(null);
return users.map(user => (
<UserItem
key={user.id}
user={user}
selected={selectedId === user.id}
onSelect={() => setSelectedId(user.id)}
/>
));
}Memoization Strategies
// Only memo when there's a measurable benefit
const UserItem = memo(function UserItem({
user,
selected,
onSelect
}: {
user: User;
selected: boolean;
onSelect: () => void;
}) {
return (
<div
onClick={onSelect}
className={selected ? 'selected' : ''}
>
{user.name}
</div>
);
});
// useMemo for expensive computations
function ExpensiveList({ items }: { items: Item[] }) {
const processed = useMemo(() => {
return items
.filter(complexFilter)
.sort(complexSort)
.map(complexTransform);
}, [items]);
return <List items={processed} />;
}
// useCallback for stable references passed to children
function Parent() {
const [items, setItems] = useState<Item[]>([]);
const handleDelete = useCallback((id: string) => {
setItems(prev => prev.filter(item => item.id !== id));
}, []);
return <ItemList items={items} onDelete={handleDelete} />;
}State Management Patterns
Context with Reducer
// types
interface State {
user: User | null;
isLoading: boolean;
error: Error | null;
}
type Action =
| { type: 'FETCH_START' }
| { type: 'FETCH_SUCCESS'; user: User }
| { type: 'FETCH_ERROR'; error: Error }
| { type: 'LOGOUT' };
// reducer
function reducer(state: State, action: Action): State {
switch (action.type) {
case 'FETCH_START':
return { ...state, isLoading: true, error: null };
case 'FETCH_SUCCESS':
return { ...state, isLoading: false, user: action.user };
case 'FETCH_ERROR':
return { ...state, isLoading: false, error: action.error };
case 'LOGOUT':
return { ...state, user: null };
}
}
// context
const AuthContext = createContext<{
state: State;
dispatch: Dispatch<Action>;
} | null>(null);
// provider
function AuthProvider({ children }: { children: ReactNode }) {
const [state, dispatch] = useReducer(reducer, {
user: null,
isLoading: true,
error: null,
});
return (
<AuthContext.Provider value={{ state, dispatch }}>
{children}
</AuthContext.Provider>
);
}
// hook
function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}Custom Hooks
// Data fetching hook
function useQuery<T>(
key: string,
fetcher: () => Promise<T>
): { data: T | null; isLoading: boolean; error: Error | null; refetch: () => void } {
const [data, setData] = useState<T | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const fetchData = useCallback(async () => {
setIsLoading(true);
setError(null);
try {
const result = await fetcher();
setData(result);
} catch (err) {
setError(err instanceof Error ? err : new Error('Unknown error'));
} finally {
setIsLoading(false);
}
}, [fetcher]);
useEffect(() => {
fetchData();
}, [fetchData]);
return { data, isLoading, error, refetch: fetchData };
}
// Debounced value hook
function useDebouncedValue<T>(value: T, delay: number): T {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebounced(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debounced;
}
// Local storage hook
function useLocalStorage<T>(
key: string,
initialValue: T
): [T, (value: T | ((prev: T) => T)) => void] {
const [storedValue, setStoredValue] = useState<T>(() => {
if (typeof window === 'undefined') return initialValue;
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch {
return initialValue;
}
});
const setValue = useCallback((value: T | ((prev: T) => T)) => {
setStoredValue(prev => {
const newValue = value instanceof Function ? value(prev) : value;
localStorage.setItem(key, JSON.stringify(newValue));
return newValue;
});
}, [key]);
return [storedValue, setValue];
}Component Patterns
Compound Components
// Flexible API with compound components
const Tabs = ({ children, defaultValue }: { children: ReactNode; defaultValue: string }) => {
const [activeTab, setActiveTab] = useState(defaultValue);
return (
<TabsContext.Provider value={{ activeTab, setActiveTab }}>
<div className="tabs">{children}</div>
</TabsContext.Provider>
);
};
Tabs.List = function TabsList({ children }: { children: ReactNode }) {
return <div className="tabs-list">{children}</div>;
};
Tabs.Tab = function Tab({ value, children }: { value: string; children: ReactNode }) {
const { activeTab, setActiveTab } = useTabsContext();
return (
<button
className={activeTab === value ? 'active' : ''}
onClick={() => setActiveTab(value)}
>
{children}
</button>
);
};
Tabs.Panel = function TabsPanel({ value, children }: { value: string; children: ReactNode }) {
const { activeTab } = useTabsContext();
if (activeTab !== value) return null;
return <div className="tabs-panel">{children}</div>;
};
// Usage
<Tabs defaultValue="tab1">
<Tabs.List>
<Tabs.Tab value="tab1">Tab 1</Tabs.Tab>
<Tabs.Tab value="tab2">Tab 2</Tabs.Tab>
</Tabs.List>
<Tabs.Panel value="tab1">Content 1</Tabs.Panel>
<Tabs.Panel value="tab2">Content 2</Tabs.Panel>
</Tabs>Render Props & Children as Function
// Data provider with render prop
function DataProvider<T>({
fetcher,
children,
}: {
fetcher: () => Promise<T>;
children: (data: T | null, isLoading: boolean) => ReactNode;
}) {
const { data, isLoading } = useQuery('data', fetcher);
return <>{children(data, isLoading)}</>;
}
// Usage
<DataProvider fetcher={getUsers}>
{(users, isLoading) => (
isLoading ? <Spinner /> : <UserList users={users} />
)}
</DataProvider>Best Practices Checklist
- [ ] Use Server Components by default, Client Components only when needed
- [ ] Push state down to the lowest component that needs it
- [ ] Break large components into smaller, focused ones
- [ ] Use Suspense boundaries for async operations
- [ ] Memoize only when profiling shows benefit
- [ ] Create custom hooks for reusable stateful logic
- [ ] Use discriminated unions for component state
- [ ] Implement proper error boundaries
- [ ] Ensure accessibility (ARIA, keyboard navigation)
- [ ] Use proper loading and error states
Related skills
FAQ
What does react-nextjs-patterns cover?
react-nextjs-patterns covers React component implementation, Next.js pages and API routes, state management, performance optimization, and Server versus Client Component decisions in the Next.js App Router.
When should developers invoke react-nextjs-patterns?
Developers should invoke react-nextjs-patterns when building frontend components, Next.js App Router pages, or choosing server and client rendering boundaries for interactive React applications.