Phase 1b: Rename all ~94 commands across 12 plugins to /<noun> <action> sub-command pattern. Git-flow consolidated from 8→5 commands (commit variants absorbed into --push/--merge/--sync flags). Dispatch files, name: frontmatter, and cross-reference updates for all plugins. Phase 2: Design documents for 8 new plugins in docs/designs/. Phase 3: Scaffold 8 new plugins — saas-api-platform, saas-db-migrate, saas-react-platform, saas-test-pilot, data-seed, ops-release-manager, ops-deploy-pipeline, debug-mcp. Each with plugin.json, commands, agents, skills, README, and claude-md-integration. Marketplace grows from 12→20. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3.9 KiB
3.9 KiB
name, description
| name | description |
|---|---|
| component-patterns | Component structure conventions including functional components, prop typing, exports, and co-located tests |
Component Patterns
Purpose
Define standard patterns for React component scaffolding. This skill ensures all generated components follow consistent structure, typing, export conventions, and test co-location.
Component File Structure
Every component file follows this order:
// 1. Imports (external first, then internal, then styles)
import { type FC } from 'react';
import { Button } from '@/components/ui/Button';
import styles from './ComponentName.module.css';
// 2. Types (inline for simple, separate file for complex)
interface ComponentNameProps {
title: string;
onAction: () => void;
children?: React.ReactNode;
}
// 3. Component definition
/**
* Brief description of what this component does.
*
* @component
* @example
* <ComponentName title="Hello" onAction={() => console.log('clicked')} />
*/
const ComponentName: FC<ComponentNameProps> = ({ title, onAction, children }) => {
return (
<div>
<h2>{title}</h2>
{children}
<button onClick={onAction}>Action</button>
</div>
);
};
// 4. Display name (for DevTools)
ComponentName.displayName = 'ComponentName';
// 5. Export
export default ComponentName;
Component Type Templates
UI Component (presentational)
- Props in, JSX out — no side effects, no data fetching
- Pure function: same props always produce same output
- Accept
classNameprop for style override flexibility - Accept
childrenif component is a container/wrapper
Page Component
- Includes data fetching (server component in App Router,
useEffectin client) - Loading state with skeleton placeholder
- Error state with retry action
'use client'directive only if client interactivity required (App Router)
Layout Component
- Accepts
children: React.ReactNodeas required prop - Optional slot props for sidebar, header, footer
- Handles responsive behavior
- Wraps with error boundary
Form Component
- Controlled inputs with
useStateor form library (react-hook-form) - Typed form values interface
- Validation schema (Zod recommended)
- Submit handler with loading state
- Error display per field and form-level
Test Co-location Patterns
Test file sits next to component file:
src/components/Button/
Button.tsx
Button.test.tsx
Button.module.css (if CSS Modules)
index.ts (barrel file)
Minimum Test Coverage
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import ComponentName from './ComponentName';
describe('ComponentName', () => {
it('renders without crashing', () => {
render(<ComponentName title="Test" onAction={() => {}} />);
expect(screen.getByText('Test')).toBeInTheDocument();
});
it('calls onAction when button clicked', async () => {
const onAction = vi.fn();
render(<ComponentName title="Test" onAction={onAction} />);
await userEvent.click(screen.getByRole('button'));
expect(onAction).toHaveBeenCalledOnce();
});
});
Barrel File Convention
Each component directory exports through index.ts:
export { default as ComponentName } from './ComponentName';
export type { ComponentNameProps } from './ComponentName';
Anti-Patterns to Avoid
| Pattern | Why | Alternative |
|---|---|---|
| Class components | Legacy API, verbose | Functional components + hooks |
React.FC with children |
Children always optional, incorrect type narrowing | Explicit children prop in interface |
Prop spreading {...props} |
Obscures expected interface | Explicitly destructure needed props |
useEffect for derived state |
Unnecessary render cycle | Compute during render or useMemo |
forwardRef without displayName |
Unnamed in DevTools | Always set displayName |