Development
Skill
/react-web
React web development with hooks, React Query, Zustand
Claude Code
$ npx -y skills add alinaqi/maggy --skill react-web --agent claude-code
Codex
$ npx skills add alinaqi/maggy --skill react-web -a codex -y
Cursor
$ npx skills add alinaqi/maggy --skill react-web -a cursor -y
Gemini CLI
$ npx skills add alinaqi/maggy --skill react-web -a gemini-cli -y
GitHub Copilot
$ npx skills add alinaqi/maggy --skill react-web -a github-copilot -y
OpenCode
$ npx skills add alinaqi/maggy --skill react-web -a opencode -y
Windsurf
$ npx skills add alinaqi/maggy --skill react-web -a windsurf -y
SKILL.md
react-web.SKILL.mdname: react-web
description: React web development with hooks, React Query, Zustand
when-to-use: When working on React web components or pages
user-invocable: false
paths: ["**/*.tsx", "**/*.jsx", "src/components/**", "src/pages/**", "src/app/**"]
effort: medium
React Web Skill
---
Test-First Development (MANDATORY)
**CRITICAL: Tests MUST be written BEFORE implementation code. This is non-negotiable for frontend components.**
The TFD Workflow
1. Write test file first → Defines expected behavior
2. Run test (it fails) → Confirms test is valid
3. Write minimal code → Just enough to pass
4. Run test (it passes) → Validates implementation
5. Refactor if needed → Tests catch regressions
Component Development Order
# CORRECT ORDER - Test first
1. Create Button.test.tsx # Write tests for expected behavior
2. Run tests (they fail) # npm test -- Button
3. Create Button.tsx # Implement to pass tests
4. Run tests (they pass) # Verify implementation
5. Create Button.module.css # Style after logic works
# WRONG ORDER - Never do this
1. Create Button.tsx # ❌ No tests exist yet
2. Create Button.module.css # ❌ Still no tests
3. "I'll add tests later" # ❌ Tests never get written
Test File Structure (Create First)
// Button.test.tsx - CREATE THIS FIRST
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';
describe('Button', () => {
// Define ALL expected behaviors upfront
describe('rendering', () => {
it('renders with label', () => {
render(<Button label="Click me" onClick={() => {}} />);
expect(screen.getByRole('button', { name: 'Click me' })).toBeInTheDocument();
});
it('applies variant class', () => {
render(<Button label="Click" onClick={() => {}} variant="secondary" />);
expect(screen.getByRole('button')).toHaveClass('secondary');
});
});
describe('interactions', () => {
it('calls onClick when clicked', () => {
const onClick = vi.fn();
render(<Button label="Click me" onClick={onClick} />);
fireEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledTimes(1);
});
it('does not call onClick when disabled', () => {
const onClick = vi.fn();
render(<Button label="Click me" onClick={onClick} disabled />);
fireEvent.click(screen.getByRole('button'));
expect(onClick).not.toHaveBeenCalled();
});
});
describe('accessibility', () => {
it('has correct aria attributes when disabled', () => {
render(<Button label="Click" onClick={() => {}} disabled />);
expect(screen.getByRole('button')).toHaveAttribute('aria-disabled', 'true');
});
});
});Hook Test First Pattern
// useCounter.test.ts - CREATE THIS FIRST
import { renderHook, act } from '@testing-library/react';
import { useCounter } from './useCounter';
describe('useCounter', () => {
it('starts at initial value', () => {
const { result } = renderHook(() => useCounter(5));
expect(result.current.count).toBe(5);
});
it('increments', () => {
const { result } = renderHook(() => useCounter());
act(() => result.current.increment());
expect(result.current.count).toBe(1);
});
it('decrements', () => {
const { result } = renderHook(() => useCounter(5));
act(() => result.current.decrement());
expect(result.current.count).toBe(4);
});
it('resets to initial value', () => {
const { result } = renderHook(() => useCounter(10));
act(() => result.current.increment());
act(() => result.current.reset());
expect(result.current.count).toBe(10);
});
});Enforcement Checklist
Before writing ANY component/hook implementation:
- [ ] Test file exists: `Component.test.tsx`
- [ ] All expected behaviors have test cases
- [ ] Tests run and FAIL (proves tests are valid)
- [ ] Only THEN create implementation file
**If tests are skipped, Claude MUST:**
⚠️ TEST-FIRST VIOLATION
Cannot create [Component].tsx - no test file exists.
Creating [Component].test.tsx first with tests for:
- Rendering with required props
- User interactions
- Edge cases
- Accessibility
---
Project Structure
project/
├── src/
│ ├── core/ # Pure business logic (no React)
│ │ ├── types.ts
│ │ └── services/
│ ├── components/ # Reusable UI components
│ │ ├── Button/
│ │ │ ├── Button.tsx
│ │ │ ├── Button.test.tsx
│ │ │ ├── Button.module.css # or .styles.ts
│ │ │ └── index.ts
│ │ └── index.ts # Barrel export
│ ├── pages/ # Route-level components
│ │ ├── Home/
│ │ │ ├── HomePage.tsx
│ │ │ ├── useHome.ts # Page-specific hook
│ │ │ └── index.ts
│ │ └── index.ts
│ ├── hooks/ # Shared custom hooks
│ ├── store/ # State management
│ ├── api/ # API client and queries
│ ├── utils/ # Utilities
│ ├── App.tsx
│ └── main.tsx
├── tests/
│ ├── unit/
│ └── e2e/
├── public/
├── package.json
├── tsconfig.json
├── vite.config.ts # or next.config.js
└── CLAUDE.md
---
Component Patterns
Functional Components Only
// Good - simple, testable
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
variant?: 'primary' | 'secondary';
}
export function Button({
label,
onClick,
disabled = false,
variant = 'primary'
}: ButtonProps): JSX.Element {
return (
<button
className={styles[variant]}
onClick={onClick}
disabled={disabled}
>
{label}
</button>
);
}Extract Logic to Hooks
// useHome.ts - all logic here
export function useHome() {
const [items, setItems] = useState<Item[]>([]);
const [loading, setLoading] = useState(false);
const refresh = useCallback(async () => {Read more
name: react-web description: React web development with hooks, React Query, Zustand when-to-use: When working on React web components or pages user-invocable: false paths: ["**/*.tsx", "**/*.jsx", "src/components/**", "src/pages/**", "src/app/**"] effort: medium
React Web Skill
---
Test-First Development (MANDATORY)
**CRITICAL: Tests MUST be written BEFORE implementation code. This is non-negotiable for frontend components.**
The TFD Workflow
1. Write test file first → Defines expected behavior 2. Run test (it fails) → Confirms test is valid 3. Write minimal code → Just enough to pass 4. Run test (it passes) → Validates implementation 5. Refactor if needed → Tests catch regressions
Component Development Order
# CORRECT ORDER - Test first 1. Create Button.test.tsx # Write tests for expected behavior 2. Run tests (they fail) # npm test -- Button 3. Create Button.tsx # Implement to pass tests 4. Run tests (they pass) # Verify implementation 5. Create Button.module.css # Style after logic works # WRONG ORDER - Never do this 1. Create Button.tsx # ❌ No tests exist yet 2. Create Button.module.css # ❌ Still no tests 3. "I'll add tests later" # ❌ Tests never get written
Test File Structure (Create First)
// Button.test.tsx - CREATE THIS FIRST
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';
describe('Button', () => {
// Define ALL expected behaviors upfront
describe('rendering', () => {
it('renders with label', () => {
render(<Button label="Click me" onClick={() => {}} />);
expect(screen.getByRole('button', { name: 'Click me' })).toBeInTheDocument();
});
it('applies variant class', () => {
render(<Button label="Click" onClick={() => {}} variant="secondary" />);
expect(screen.getByRole('button')).toHaveClass('secondary');
});
});
describe('interactions', () => {
it('calls onClick when clicked', () => {
const onClick = vi.fn();
render(<Button label="Click me" onClick={onClick} />);
fireEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledTimes(1);
});
it('does not call onClick when disabled', () => {
const onClick = vi.fn();
render(<Button label="Click me" onClick={onClick} disabled />);
fireEvent.click(screen.getByRole('button'));
expect(onClick).not.toHaveBeenCalled();
});
});
describe('accessibility', () => {
it('has correct aria attributes when disabled', () => {
render(<Button label="Click" onClick={() => {}} disabled />);
expect(screen.getByRole('button')).toHaveAttribute('aria-disabled', 'true');
});
});
});Hook Test First Pattern
// useCounter.test.ts - CREATE THIS FIRST
import { renderHook, act } from '@testing-library/react';
import { useCounter } from './useCounter';
describe('useCounter', () => {
it('starts at initial value', () => {
const { result } = renderHook(() => useCounter(5));
expect(result.current.count).toBe(5);
});
it('increments', () => {
const { result } = renderHook(() => useCounter());
act(() => result.current.increment());
expect(result.current.count).toBe(1);
});
it('decrements', () => {
const { result } = renderHook(() => useCounter(5));
act(() => result.current.decrement());
expect(result.current.count).toBe(4);
});
it('resets to initial value', () => {
const { result } = renderHook(() => useCounter(10));
act(() => result.current.increment());
act(() => result.current.reset());
expect(result.current.count).toBe(10);
});
});Enforcement Checklist
Before writing ANY component/hook implementation:
- [ ] Test file exists: `Component.test.tsx`
- [ ] All expected behaviors have test cases
- [ ] Tests run and FAIL (proves tests are valid)
- [ ] Only THEN create implementation file
**If tests are skipped, Claude MUST:**
⚠️ TEST-FIRST VIOLATION Cannot create [Component].tsx - no test file exists. Creating [Component].test.tsx first with tests for: - Rendering with required props - User interactions - Edge cases - Accessibility
---
Project Structure
project/ ├── src/ │ ├── core/ # Pure business logic (no React) │ │ ├── types.ts │ │ └── services/ │ ├── components/ # Reusable UI components │ │ ├── Button/ │ │ │ ├── Button.tsx │ │ │ ├── Button.test.tsx │ │ │ ├── Button.module.css # or .styles.ts │ │ │ └── index.ts │ │ └── index.ts # Barrel export │ ├── pages/ # Route-level components │ │ ├── Home/ │ │ │ ├── HomePage.tsx │ │ │ ├── useHome.ts # Page-specific hook │ │ │ └── index.ts │ │ └── index.ts │ ├── hooks/ # Shared custom hooks │ ├── store/ # State management │ ├── api/ # API client and queries │ ├── utils/ # Utilities │ ├── App.tsx │ └── main.tsx ├── tests/ │ ├── unit/ │ └── e2e/ ├── public/ ├── package.json ├── tsconfig.json ├── vite.config.ts # or next.config.js └── CLAUDE.md
---
Component Patterns
Functional Components Only
// Good - simple, testable
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
variant?: 'primary' | 'secondary';
}
export function Button({
label,
onClick,
disabled = false,
variant = 'primary'
}: ButtonProps): JSX.Element {
return (
<button
className={styles[variant]}
onClick={onClick}
disabled={disabled}
>
{label}
</button>
);
}Extract Logic to Hooks
// useHome.ts - all logic here
export function useHome() {
const [items, setItems] = useState<Item[]>([]);
const [loading, setLoading] = useState(false);
const refresh = useCallback(async () => { Stats
712
Stars
63
Forks
Maintained
Maintenance
Python
Language
MIT
License
Last commit
Created
Ships withmaggy
Turn Claude Code into a self-reviewing, test-enforced engineering system that remembers context across sessions — then route work across 13 models from a single dashboard.
71073 skills1 agent
Other skills on maggy.
More skills from other plugins.
Auto-invoked
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Auto-invoked
Questions about react-web.
- Which coding agents can use react-web?
- react-web installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

