Claude Code Plugins

Community-maintained marketplace

Feedback

React best practices expert. PROACTIVELY use when working with React components, hooks, state management. Triggers: React, JSX, hooks, useState, useEffect, component

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name react-expert
description React best practices expert. PROACTIVELY use when working with React components, hooks, state management. Triggers: React, JSX, hooks, useState, useEffect, component
autoInvoke true
priority high
triggers react, jsx, component, useState, useEffect, hooks
allowed-tools Read, Grep, Glob, Edit, Write

React Expert Skill

Expert-level React patterns, hooks best practices, performance optimization, and component architecture.


Auto-Detection

This skill activates when:

  • Working with .jsx, .tsx React files
  • Using React hooks (useState, useEffect, etc.)
  • Building React components
  • Detected react in package.json

1. Component Patterns

Function Components Only

// ❌ 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>;
};

Props Interface Pattern

// ✅ 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>
  );
}

Compound Components

// ✅ 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>

2. Hooks Best Practices

useState

// ❌ 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());

useEffect

// ❌ 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]);

useMemo & useCallback

// ❌ 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);

Custom Hooks

// ✅ 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 };
}

3. Conditional Rendering

Safe Patterns

// ❌ 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>;
}

List Rendering

// ❌ 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" />
)}

4. State Management

Local vs Global State

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

Context Pattern

// ✅ 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>
  );
}

5. Performance Optimization

Prevent Unnecessary Re-renders

// ✅ 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 />
    </>
  );
}

Code Splitting

// ✅ 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>
  );
}

6. Form Handling

Controlled Components

// ✅ 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>
  );
}

Form Libraries

// ✅ 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>
  );
}

7. Error Boundaries

// ✅ 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>

Quick Reference

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