Back to list
AlexanderStephenThompson

web-react

by AlexanderStephenThompson

Personal Claude Code customizations: multi-agent teams, skills, agents, and commands

0🍴 0📅 Jan 22, 2026

SKILL.md


name: web-react description: React patterns for vanilla React with Apollo Client - components, hooks, state management user-invocable: false

Web React Skill

Version: 1.0 Stack: React (vanilla) + Apollo Client

React patterns optimized for Apollo-powered applications. No framework abstractions—just clean React.


Core Principles

  1. Components Stay Small — Under 200 lines. If larger, split it.
  2. Hooks for Logic — Extract business logic into custom hooks.
  3. Apollo for Server State — Don't duplicate server state in local state.
  4. Props Down, Events Up — Clear data flow, no prop drilling beyond 2 levels.
  5. Colocation — Keep related code together (component + styles + tests).

Component Patterns

File Size Guidelines

SizeStatusAction
< 100 lines✅ IdealKeep it
100-200 lines⚠️ WatchConsider splitting if growing
> 200 lines❌ Too bigSplit into smaller components
> 300 lines🚨 CriticalImmediate refactor needed

Component Structure

// ✅ Good - Clear structure
function ProductCard({ product, onAddToCart }) {
  const [quantity, setQuantity] = useState(1);

  const handleAdd = () => {
    onAddToCart(product.id, quantity);
  };

  return (
    <article className="product-card">
      <img src={product.image} alt={product.name} />
      <h3>{product.name}</h3>
      <p>{product.description}</p>
      <div className="product-card__actions">
        <input
          type="number"
          value={quantity}
          onChange={(e) => setQuantity(Number(e.target.value))}
          min={1}
        />
        <button onClick={handleAdd}>Add to Cart</button>
      </div>
    </article>
  );
}

When to Split Components

Split when you see:

  • Multiple responsibilities in one component
  • Reusable UI patterns
  • Complex conditional rendering
  • Deeply nested JSX (> 4 levels)
// ❌ Too much in one component
function ProductPage() {
  // 50 lines of hooks...
  // 100 lines of handlers...
  // 200 lines of JSX...
}

// ✅ Split by responsibility
function ProductPage() {
  return (
    <main>
      <ProductHeader />
      <ProductGallery />
      <ProductDetails />
      <ProductReviews />
      <RelatedProducts />
    </main>
  );
}

Hooks Patterns

Custom Hooks for Logic

Extract logic that:

  • Uses multiple hooks together
  • Contains business logic
  • Could be reused
  • Makes components hard to read
// ✅ Good - Logic extracted to hook
function useProductQuantity(initialQuantity = 1) {
  const [quantity, setQuantity] = useState(initialQuantity);

  const increment = () => setQuantity(q => q + 1);
  const decrement = () => setQuantity(q => Math.max(1, q - 1));
  const reset = () => setQuantity(initialQuantity);

  return { quantity, setQuantity, increment, decrement, reset };
}

// Component stays clean
function QuantitySelector({ onChange }) {
  const { quantity, increment, decrement } = useProductQuantity();

  useEffect(() => {
    onChange(quantity);
  }, [quantity, onChange]);

  return (
    <div className="quantity-selector">
      <button onClick={decrement}>-</button>
      <span>{quantity}</span>
      <button onClick={increment}>+</button>
    </div>
  );
}

Hook Rules (Enforced)

  1. Only call hooks at the top level
  2. Only call hooks from React functions
  3. Custom hooks must start with use
  4. Dependencies must be exhaustive (ESLint rule)

Apollo Client Patterns

Server State vs Local State

Data TypeWhere to StoreExample
User data from APIApollo cacheProfile, preferences
List data from APIApollo cacheProducts, orders
Form input before submitLocal stateInput values
UI stateLocal stateModal open, sidebar collapsed
Derived from server dataComputedFiltered list, totals

Query Patterns

// ✅ Good - Using Apollo hooks
function ProductList({ categoryId }) {
  const { data, loading, error } = useQuery(GET_PRODUCTS, {
    variables: { categoryId },
    // Stale-while-revalidate pattern
    fetchPolicy: 'cache-and-network',
  });

  if (loading && !data) return <ProductListSkeleton />;
  if (error) return <ErrorMessage error={error} />;

  return (
    <ul className="product-list">
      {data.products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </ul>
  );
}

Mutation Patterns

// ✅ Good - Optimistic updates
function AddToCartButton({ productId }) {
  const [addToCart, { loading }] = useMutation(ADD_TO_CART, {
    variables: { productId },
    optimisticResponse: {
      addToCart: {
        __typename: 'CartItem',
        id: 'temp-id',
        productId,
        quantity: 1,
      },
    },
    update(cache, { data: { addToCart } }) {
      // Update cart cache
      cache.modify({
        fields: {
          cart(existingCart = []) {
            const newItemRef = cache.writeFragment({
              data: addToCart,
              fragment: CART_ITEM_FRAGMENT,
            });
            return [...existingCart, newItemRef];
          },
        },
      });
    },
  });

  return (
    <button onClick={() => addToCart()} disabled={loading}>
      {loading ? 'Adding...' : 'Add to Cart'}
    </button>
  );
}

Don't Duplicate Server State

// ❌ Bad - Duplicating Apollo data in local state
function ProductList() {
  const { data } = useQuery(GET_PRODUCTS);
  const [products, setProducts] = useState([]); // Why?

  useEffect(() => {
    if (data) setProducts(data.products); // Duplication!
  }, [data]);
}

// ✅ Good - Use Apollo cache directly
function ProductList() {
  const { data, loading } = useQuery(GET_PRODUCTS);

  // Filter/transform inline or with useMemo
  const activeProducts = useMemo(
    () => data?.products.filter(p => p.active) ?? [],
    [data]
  );
}

State Management

When to Use What

NeedSolution
Server dataApollo Client (useQuery, useMutation)
Global UI stateReact Context
Component UI stateuseState
Complex component stateuseReducer
Form stateuseState or form library

Context Pattern (When Needed)

// ✅ Good - Focused context for specific concern
const ThemeContext = createContext();

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');

  const toggle = useCallback(() => {
    setTheme(t => t === 'light' ? 'dark' : 'light');
  }, []);

  const value = useMemo(() => ({ theme, toggle }), [theme, toggle]);

  return (
    <ThemeContext.Provider value={value}>
      {children}
    </ThemeContext.Provider>
  );
}

function useTheme() {
  const context = useContext(ThemeContext);
  if (!context) {
    throw new Error('useTheme must be used within ThemeProvider');
  }
  return context;
}

Avoid Context for Frequently Changing Data

// ❌ Bad - Causes unnecessary re-renders
const AppContext = createContext();
// Contains: user, theme, cart, notifications, sidebar state...
// Every change re-renders everything!

// ✅ Good - Split by concern
const UserContext = createContext();
const ThemeContext = createContext();
const SidebarContext = createContext();

File Organization

src/
├── components/           # Shared/reusable components
│   ├── Button/
│   │   ├── Button.jsx
│   │   ├── Button.css
│   │   └── Button.test.jsx
│   └── Modal/
├── features/             # Feature-based organization
│   ├── products/
│   │   ├── components/   # Feature-specific components
│   │   ├── hooks/        # Feature-specific hooks
│   │   ├── graphql/      # Queries and mutations
│   │   └── ProductsPage.jsx
│   └── cart/
├── hooks/                # Shared custom hooks
├── graphql/              # Shared GraphQL (fragments, client setup)
├── utils/                # Pure utility functions
└── App.jsx

Naming Conventions

TypeConventionExample
ComponentsPascalCaseProductCard.jsx
HookscamelCase with use prefixuseProductQuantity.js
UtilscamelCaseformatPrice.js
GraphQL queriesSCREAMING_SNAKEGET_PRODUCTS
GraphQL filescamelCaseproducts.graphql or products.js

Anti-Patterns

Anti-PatternProblemFix
Giant componentsHard to read, test, maintainSplit by responsibility
Prop drilling > 2 levelsTight coupling, verboseUse composition or context
useEffect for derived stateUnnecessary rendersUse useMemo or compute inline
Duplicating Apollo cacheDouble source of truthQuery directly from cache
Business logic in componentsHard to test, can't reuseExtract to hooks
Inline functions in JSXNew reference each renderuseCallback or extract
Missing loading/error statesBad UXAlways handle all states
Fetching in useEffectRace conditions, no cachingUse Apollo useQuery

Performance Checklist

  • Components < 200 lines
  • Heavy computations wrapped in useMemo
  • Callbacks wrapped in useCallback when passed as props
  • Lists have stable key props
  • Large lists use virtualization
  • Images lazy loaded
  • Code split by route (React.lazy)

When to Consider Alternatives

While this stack works well, consider alternatives when:

SituationConsider
Need SSR/SSGNext.js or Remix
Very simple appPlain React without Apollo
Real-time heavyConsider subscriptions or WebSockets
Complex formsForm library (React Hook Form)

Quick Reference

Import Order

// 1. React
import React, { useState, useCallback } from 'react';

// 2. Third-party
import { useQuery, useMutation } from '@apollo/client';
import { format } from 'date-fns';

// 3. Internal modules
import { useAuth } from '@/hooks/useAuth';
import { GET_PRODUCTS } from '@/graphql/products';

// 4. Components
import { Button } from '@/components/Button';
import { ProductCard } from './ProductCard';

// 5. Styles
import './ProductList.css';

Score

Total Score

50/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon