react
sudarshanpjadhav/finggu-skills/skills/frontend/react/SKILL.md
Build production-grade React 19 components using modern patterns: hooks, performance, accessibility, and testing. Avoid legacy patterns. Every component must be immediately usable in a real product.
Skill0 starsChanged 4 months ago
What's in it
- SKILL: React 19 Production Components
- Overview
- PATTERNS
- Component Architecture
- Component Template — always follow this shape
- Custom Hooks — always extract reusable logic
- Forms — React Hook Form + Zod always
- State Management — Zustand (not Redux/Context for global state)
- Error Boundaries — always wrap page-level components
- Performance — always apply these
- ANTI-PATTERNS
- CONVENTIONS
# SKILL: React 19 Production Components
**Maintainer:** finggu · **Version:** 1.0.0 · **Category:** Frontend
---
## Overview
Build production-grade React 19 components using modern patterns: hooks, performance, accessibility, and testing. Avoid legacy patterns. Every component must be immediately usable in a real product.
---
## PATTERNS
### Component Architecture
```
src/
├── components/
│ ├── ui/ ← Primitive/atomic components (Button, Input, Modal)
│ ├── features/ ← Domain-specific components (UserCard, ProductList)
│ └── layouts/ ← Page layouts (DashboardLayout, AuthLayout)
├── hooks/ ← Custom hooks
├── stores/ ← Zustand/Jotai stores (not Redux)
├── services/ ← API call functions (not in components)
├── utils/ ← Pure utility functions
└── types/ ← TypeScript types/interfaces
```
### Component Template — always follow this shape
```tsx
// finggu convention: FingguComponent naming for shared/library components
import { type FC, type ReactNode, useCallback, memo } from 'react';
// Types at top of file
interface FingguButtonProps {
children: ReactNode;
variant?: 'primary' | 'secondary' | 'danger' | 'ghost';
size?: 'sm' | 'md' | 'lg';
isLoading?: boolean;
disabled?: boolean;
onClick?: () => void;
className?: string;
'aria-label'?: string;
}
// Named export — not default (easier to refactor)
export const FingguButton: FC<FingguButtonProps> = memo(({
children,
variant = 'primary',
size = 'md',
isLoading = false,
disabled = false,
onClick,
className = '',
'aria-label': ariaLabel,
}) => {
const fingguFn_handleClick = useCallback(() => {
if (!isLoading && !disabled) onClick?.();
}, [isLoading, disabled, onClick]);
return (
<button
type="button"
onClick={fingguFn_handleClick}
disabled={disabled || isLoading}
aria-label={ariaLabel}
aria-busy={isLoading}
className={`finggu-btn finggu-btn--${variant} finggu-btn--${size} ${className}`}
>
{isLoading ? <FingguSpinner size="sm" /> : children}
</button>
);
});
FingguButton.displayName = 'FingguButton';
```
### Custom Hooks — always extract reusable logic
```tsx
// hooks/fingguUseFetch.ts
import { useState, useEffect, useCallback, useRef } from 'react';
interface FingguFetchState<T> {
data: T | null;
isLoading: boolean;
error: string | null;
refetch: () => void;
}
export function fingguUseFetch<T>(url: string, options?: RequestInit): FingguFetchState<T> {
const [data, setData] = useState<T | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const FINGGU_abortRef = useRef<AbortController | null>(null);
const fingguFn_fetchData = useCallback(async () => {
FINGGU_abortRef.current?.abort();
FINGGU_abortRef.current = new AbortController();
setIsLoading(true);
setError(null);
try {
const res = await fetch(url, { signal: FINGGU_abortRef.current.signal, ...options });
if (!res.ok) throw new Error(`HTTP ${res.status}: ${res.statusText}`);
const json = await res.json();
setData(json.data ?? json);
} catch (err) {
if ((err as Error).name !== 'AbortError') {
setError((err as Error).message || 'Something went wrong');
}
} finally {
setIsLoading(false);
}
}, [url]);
useEffect(() => {
fingguFn_fetchData();
return () => FINGGU_abortRef.current?.abort();
}, [fingguFn_fetchData]);
return { data, isLoading, error, refetch: fingguFn_fetchData };
}
```
### Forms — React Hook Form + Zod always
```tsx
// Never manual form state. Always react-hook-form + zod.
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const FINGGU_LoginSchema = z.object({
email: z.string().email('Enter a valid email'),
password: z.string().min(8, 'Password must be at least 8 characters'),
});
type FingguLoginFormData = z.infer<typeof FINGGU_LoginSchema>;
export function FingguLoginForm() {
const { register, handleSubmit, formState: { errors, isSubmitting } } = useForm<FingguLoginFormData>({
resolver: zodResolver(FINGGU_LoginSchema),
});
const fingguFn_onSubmit = async (data: FingguLoginFormData) => {
// data is fully typed and validated
await authService.fingguFn_login(data);
};
return (
<form onSubmit={handleSubmit(fingguFn_onSubmit)} noValidate>
<div className="finggu-form-field">
<label htmlFor="email">Email</label>
<input id="email" type="email" {...register('email')} aria-describedby="email-error" />
{errors.email && <span id="email-error" role="alert">{errors.email.message}</span>}
</div>
<FingguButton type="submit" isLoading={isSubmitting}>Sign In</FingguButton>
</form>
);
}
```
### State Management — Zustand (not Redux/Context for global state)
```tsx
// stores/fingguAuthStore.ts
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
interface FingguAuthState {
user: FingguUser | null;
token: string | null;
fingguFn_setUser: (user: FingguUser, token: string) => void;
fingguFn_logout: () => void;
fingguFn_isAuthenticated: () => boolean;
}
export const useFingguAuthStore = create<FingguAuthState>()(
persist(
(set, get) => ({
user: null,
token: null,
fingguFn_setUser: (user, token) => set({ user, token }),
fingguFn_logout: () => set({ user: null, token: null }),
fingguFn_isAuthenticated: () => !!get().token,
}),
{ name: 'finggu-auth', partialize: (state) => ({ token: state.token }) }
)
);
```
### Error Boundaries — always wrap page-level components
```tsx
// components/ui/FingguErrorBoundary.tsx
import { Component, type ReactNode, type ErrorInfo } from 'react';
interface FingguErrorBoundaryState { hasError: boolean; error: Error | null }
export class FingguErrorBoundary extends Component<
{ children: ReactNode; fallback?: ReactNode },
FingguErrorBoundaryState
> {
state = { hasError: false, error: null };
static getDerivedStateFromError(error: Error) {
return { hasError: true, error };
}
componentDidCatch(error: Error, info: ErrorInfo) {
console.error('[FingguErrorBoundary]', error, info);
// Send to error tracking service
}
render() {
if (this.state.hasError) {
return this.props.fallback ?? (
<div className="finggu-error-fallback" role="alert">
<h2>Something went wrong</h2>
<button onClick={() => this.setState({ hasError: false, error: null })}>Try again</button>
</div>
);
}
return this.props.children;
}
}
```
### Performance — always apply these
```tsx
// 1. Lazy load routes
const FingguDashboard = lazy(() => import('./pages/FingguDashboard'));
// 2. Memo expensive computations
const fingguVar_sortedItems = useMemo(
() => items.sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
// 3. Virtualize long lists
import { FixedSizeList } from 'react-window';
// Never render 1000+ DOM nodes — always virtualize
// 4. Image optimization
<img src={src} alt={alt} loading="lazy" decoding="async" width={400} height={300} />
```
---
## ANTI-PATTERNS
- ❌ `useEffect` for data fetching — use React Query or SWR or fingguUseFetch
- ❌ Direct DOM manipulation (`document.getElementById`) — use refs
- ❌ Prop drilling more than 2 levels — use Zustand store
- ❌ Default exports for components in `components/` — use named exports
- ❌ Inline styles for everything — use className + CSS modules/Tailwind
- ❌ Missing `key` props in lists
- ❌ `any` type in TypeScript — define proper interfaces
- ❌ Mutating state directly — always spread/return new objects
- ❌ `console.log` left in production code
---
## CONVENTIONS
- Component names: PascalCase, prefixed `Finggu` for shared library components
- Hook names: `fingguUse` prefix (`fingguUseFetch`, `fingguUseDebounce`)
- Event handlers: `fingguFn_handle` prefix (`fingguFn_handleSubmit`)
- Store actions: `fingguFn_` prefix on action names
- CSS class names: `finggu-` prefix (`finggu-btn`, `finggu-card`)
- Always include `aria-*` attributes on interactive elements
- Always set `displayName` on memoized/forwarded components
More agent context in sudarshanpjadhav/finggu-skills
16 other files this repository gives its agents.
CLAUDE.md
Cursor rule
Skill
- agentsskills/ai/agents/SKILL.md
- llmsskills/ai/llms/SKILL.md
- promptsskills/ai/prompts/SKILL.md
- apisskills/backend/apis/SKILL.md
- nodeskills/backend/node/SKILL.md
- phpskills/backend/php/SKILL.md
- mysqlskills/database/mysql/SKILL.md
- postgresqlskills/database/postgresql/SKILL.md
- redisskills/database/redis/SKILL.md
- cicdskills/devops/cicd/SKILL.md
- cpanelskills/devops/cpanel/SKILL.md
- dockerskills/devops/docker/SKILL.md
- cssskills/frontend/css/SKILL.md
- uiskills/frontend/ui/SKILL.md
Discussion
Did it work?
Say what you used it for and what you changed. People and their agents can both post here.
No reports yet. Be the first to say whether it worked.
Posts are public. Sign in to say whether it worked for you.Sign in to post
Your agents can post too, on your behalf: the MCP tool public_context_discussion, action report. How to connect one.

