React best practices expert. PROACTIVELY use when working with React components, hooks, state management. Triggers: React, JSX, hooks, useState, useEffect, component
/plugin marketplace add nguyenthienthanh/aura-frog/plugin install aura-frog@aurafrogThis skill is limited to using the following tools:
Expert-level React patterns, hooks best practices, performance optimization, and component architecture.
This skill activates when:
.jsx, .tsx React filesreact in package.json// ❌ BAD - Class components (legacy)
class UserCard extends React.Component { }
// ✅ GOOD - Function components
function UserCard({ user }: UserCardProps) {
return <div>{user.name}</div>;
}
// ✅ GOOD - Arrow function with explicit return type
const UserCard: React.FC<UserCardProps> = ({ user }) => {
return <div>{user.name}</div>;
};
// ✅ GOOD - Explicit props interface
interface UserCardProps {
user: User;
onSelect?: (user: User) => void;
className?: string;
children?: React.ReactNode;
}
function UserCard({
user,
onSelect,
className,
children
}: UserCardProps) {
return (
<div className={className} onClick={() => onSelect?.(user)}>
<h3>{user.name}</h3>
{children}
</div>
);
}
// ✅ GOOD - Compound component pattern
const Card = ({ children }: { children: React.ReactNode }) => (
<div className="card">{children}</div>
);
Card.Header = ({ children }: { children: React.ReactNode }) => (
<div className="card-header">{children}</div>
);
Card.Body = ({ children }: { children: React.ReactNode }) => (
<div className="card-body">{children}</div>
);
// Usage
<Card>
<Card.Header>Title</Card.Header>
<Card.Body>Content</Card.Body>
</Card>
// ❌ BAD - Object state without proper updates
const [user, setUser] = useState({ name: '', email: '' });
setUser({ name: 'John' }); // Loses email!
// ✅ GOOD - Spread previous state
setUser(prev => ({ ...prev, name: 'John' }));
// ✅ GOOD - Separate states for unrelated values
const [name, setName] = useState('');
const [email, setEmail] = useState('');
// ✅ GOOD - Lazy initialization for expensive computation
const [data, setData] = useState(() => computeExpensiveInitialValue());
// ❌ BAD - Missing dependencies
useEffect(() => {
fetchUser(userId);
}, []); // userId missing!
// ❌ BAD - Object/array in dependencies (infinite loop)
useEffect(() => {
doSomething(options);
}, [options]); // New object every render!
// ✅ GOOD - Primitive dependencies
useEffect(() => {
fetchUser(userId);
}, [userId]);
// ✅ GOOD - Cleanup function
useEffect(() => {
const subscription = subscribe(userId);
return () => {
subscription.unsubscribe();
};
}, [userId]);
// ✅ GOOD - Abort controller for async
useEffect(() => {
const controller = new AbortController();
async function fetchData() {
try {
const response = await fetch(url, { signal: controller.signal });
const data = await response.json();
setData(data);
} catch (error) {
if (error instanceof Error && error.name !== 'AbortError') {
setError(error);
}
}
}
fetchData();
return () => controller.abort();
}, [url]);
// ❌ BAD - Unnecessary memoization
const value = useMemo(() => a + b, [a, b]); // Simple math
// ✅ GOOD - Expensive computation
const sortedList = useMemo(() => {
return [...items].sort((a, b) => a.name.localeCompare(b.name));
}, [items]);
// ✅ GOOD - Stable callback for child components
const handleClick = useCallback((id: string) => {
onSelect(id);
}, [onSelect]);
// ✅ GOOD - Prevent child re-renders
const MemoizedChild = React.memo(ChildComponent);
// ✅ GOOD - Extract reusable logic
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
// ✅ GOOD - Data fetching hook
function useFetch<T>(url: string) {
const [data, setData] = useState<T | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const controller = new AbortController();
setLoading(true);
fetch(url, { signal: controller.signal })
.then(res => res.json())
.then(setData)
.catch(err => {
if (err.name !== 'AbortError') setError(err);
})
.finally(() => setLoading(false));
return () => controller.abort();
}, [url]);
return { data, loading, error };
}
// ❌ BAD - && with numbers (shows "0")
{count && <Badge count={count} />}
// ✅ GOOD - Explicit boolean
{count > 0 && <Badge count={count} />}
// ❌ BAD - && with strings (shows empty string issues)
{title && <Header title={title} />}
// ✅ GOOD - Ternary for clarity
{title ? <Header title={title} /> : null}
// ✅ GOOD - Nullish check
{title != null && title !== '' && <Header title={title} />}
// ✅ GOOD - Early return pattern
function UserProfile({ user }: { user: User | null }) {
if (user == null) {
return <LoadingSpinner />;
}
return <div>{user.name}</div>;
}
// ❌ BAD - Index as key (causes issues with reordering)
{items.map((item, index) => <Item key={index} item={item} />)}
// ✅ GOOD - Unique ID as key
{items.map(item => <Item key={item.id} item={item} />)}
// ✅ GOOD - Empty state handling
{items.length > 0 ? (
items.map(item => <Item key={item.id} item={item} />)
) : (
<EmptyState message="No items found" />
)}
state_decision[5]{type,use_when,solution}:
Local state,Component-specific UI,useState
Lifted state,Shared between siblings,Lift to parent
Context,Theme/auth/deep props,React Context
Server state,API data,TanStack Query/SWR
Global state,Complex app state,Zustand/Redux
// ✅ GOOD - Typed context with provider
interface AuthContextType {
user: User | null;
login: (credentials: Credentials) => Promise<void>;
logout: () => void;
}
const AuthContext = createContext<AuthContextType | null>(null);
export function useAuth() {
const context = useContext(AuthContext);
if (context == null) {
throw new Error('useAuth must be used within AuthProvider');
}
return context;
}
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const login = useCallback(async (credentials: Credentials) => {
const user = await authApi.login(credentials);
setUser(user);
}, []);
const logout = useCallback(() => {
setUser(null);
}, []);
const value = useMemo(() => ({ user, login, logout }), [user, login, logout]);
return (
<AuthContext.Provider value={value}>
{children}
</AuthContext.Provider>
);
}
// ✅ GOOD - Memoize expensive components
const ExpensiveList = React.memo(function ExpensiveList({ items }: Props) {
return items.map(item => <ExpensiveItem key={item.id} item={item} />);
});
// ✅ GOOD - Custom comparison
const UserCard = React.memo(
function UserCard({ user }: { user: User }) {
return <div>{user.name}</div>;
},
(prevProps, nextProps) => prevProps.user.id === nextProps.user.id
);
// ✅ GOOD - Split components to isolate re-renders
function Parent() {
return (
<>
<FrequentlyUpdating />
<ExpensiveButStatic />
</>
);
}
// ✅ GOOD - Lazy load routes/components
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
);
}
// ✅ GOOD - Controlled with proper types
function LoginForm({ onSubmit }: { onSubmit: (data: LoginData) => void }) {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [errors, setErrors] = useState<Record<string, string>>({});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const newErrors: Record<string, string> = {};
if (email === '') newErrors.email = 'Email is required';
if (password === '') newErrors.password = 'Password is required';
if (Object.keys(newErrors).length > 0) {
setErrors(newErrors);
return;
}
onSubmit({ email, password });
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={e => setEmail(e.target.value)}
aria-invalid={errors.email != null}
/>
{errors.email != null && <span role="alert">{errors.email}</span>}
{/* ... */}
</form>
);
}
// ✅ GOOD - React Hook Form + Zod
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
type FormData = z.infer<typeof schema>;
function LoginForm() {
const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
resolver: zodResolver(schema),
});
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input {...register('email')} />
{errors.email && <span>{errors.email.message}</span>}
</form>
);
}
// ✅ GOOD - Error boundary component
class ErrorBoundary extends React.Component<
{ children: React.ReactNode; fallback: React.ReactNode },
{ hasError: boolean }
> {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
componentDidCatch(error: Error, info: React.ErrorInfo) {
console.error('Error caught:', error, info);
// Log to error tracking service
}
render() {
if (this.state.hasError) {
return this.props.fallback;
}
return this.props.children;
}
}
// Usage
<ErrorBoundary fallback={<ErrorPage />}>
<App />
</ErrorBoundary>
checklist[10]{pattern,do_this}:
Component type,Function components only
Props,Interface with explicit types
Keys,Unique IDs not indices
useEffect deps,Include all dependencies
Conditional &&,Use explicit boolean check
State updates,Spread previous for objects
Memoization,Only for expensive operations
Context,Throw if used outside provider
Forms,Controlled with validation
Errors,Error boundaries at route level
Version: 1.3.0
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.