Loading...
Loading...
Use when the user needs production-grade React/Next.js/TypeScript development with rigorous component architecture, state management, performance optimization, and >85% test coverage. Triggers: React component development, Next.js page creation, state management design, frontend performance audit, component library setup.
npx skill4agent add pixel-process-ug/superkit-agents senior-frontend| Level | Description | Business Logic | Example |
|---|---|---|---|
| Atoms | Smallest building blocks | None | Button, Input, Icon, Badge |
| Molecules | Composed of atoms | Minimal | FormField, SearchBar, Card |
| Organisms | Complex with business logic | Yes | DataTable, NavigationBar, CommentThread |
| Templates | Page structure without data | Layout only | DashboardLayout, AuthLayout |
| Pages | Templates connected to data | Data fetching | UsersPage, SettingsPage |
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'ghost' | 'danger';
size?: 'sm' | 'md' | 'lg';
isLoading?: boolean;
}
export function Button({ variant = 'primary', size = 'md', isLoading, children, ...props }: ButtonProps) {
return (
<button className={cn(buttonVariants({ variant, size }))} disabled={isLoading || props.disabled} {...props}>
{isLoading ? <Spinner size={size} /> : children}
</button>
);
}| State Type | Solution | When to Use |
|---|---|---|
| Server state | React Query / TanStack Query | API data, caching, sync |
| Form state | React Hook Form + Zod | Form validation, submission |
| Global UI state | Zustand | Theme, sidebar open, modals |
| Local UI state | useState / useReducer | Component-specific state |
| URL state | nuqs / useSearchParams | Filters, pagination, tabs |
| Complex local | useReducer | Multiple related state transitions |
| Shared context | React Context | Theme, locale, auth (infrequent updates) |
| Pattern | Use When | Cache Strategy |
|---|---|---|
| Static (SSG) | Content rarely changes | Build time |
| ISR | Content changes periodically | Revalidate interval |
| SSR | Content changes per request | No cache |
| Client | User-specific, interactive | Browser |
| Need | Component Type |
|---|---|
| Direct data fetching | Server (default) |
| Event handlers (onClick, onChange) | Client ( |
| useState / useReducer | Client |
| useEffect / useLayoutEffect | Client |
| Browser APIs (window, localStorage) | Client |
| Third-party libs using client features | Client |
| No interactivity needed | Server (default) |
any{
"coverageThreshold": {
"global": {
"branches": 85,
"functions": 85,
"lines": 85,
"statements": 85
}
}
}describe('Button', () => {
it('renders children', () => {
render(<Button>Click me</Button>);
expect(screen.getByRole('button', { name: 'Click me' })).toBeInTheDocument();
});
it('shows loading state', () => {
render(<Button isLoading>Click me</Button>);
expect(screen.getByRole('button')).toBeDisabled();
});
it('calls onClick when clicked', async () => {
const onClick = vi.fn();
render(<Button onClick={onClick}>Click me</Button>);
await userEvent.click(screen.getByRole('button'));
expect(onClick).toHaveBeenCalledOnce();
});
});test('user can complete checkout', async ({ page }) => {
await page.goto('/products');
await page.getByRole('button', { name: 'Add to cart' }).first().click();
await page.getByRole('link', { name: 'Cart' }).click();
await expect(page.getByText('1 item')).toBeVisible();
await page.getByRole('button', { name: 'Checkout' }).click();
});function useUsers(filters: UserFilters) {
return useQuery({
queryKey: ['users', filters],
queryFn: () => fetchUsers(filters),
staleTime: 5 * 60 * 1000,
placeholderData: keepPreviousData,
});
}
function useUpdateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateUser,
onMutate: async (newUser) => {
await queryClient.cancelQueries({ queryKey: ['users'] });
const previous = queryClient.getQueryData(['users']);
queryClient.setQueryData(['users'], (old) =>
old.map(u => u.id === newUser.id ? { ...u, ...newUser } : u)
);
return { previous };
},
onError: (err, newUser, context) => {
queryClient.setQueryData(['users'], context.previous);
},
onSettled: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
}| Technique | Use When | Do NOT Use When |
|---|---|---|
| Expensive computation, referential equality for deps | Simple calculations, primitive values |
| Functions passed to memoized children | Functions not passed as props |
| Component re-renders often with same props | Props change on every render |
| None | Default — do not memoize | Always profile first |
| Anti-Pattern | Why It Is Wrong | Correct Approach |
|---|---|---|
| Race conditions, no caching, no dedup | React Query or Server Components |
| Prop drilling more than 2 levels | Tight coupling, maintenance burden | Composition, context, or Zustand |
| Business logic in components | Untestable, unreusable | Extract to hooks or utility functions |
| Barrel exports | Breaks tree-shaking, slower builds | Direct imports |
| Testing implementation details | Brittle tests that break on refactor | Test behavior: user actions and outcomes |
| Defeats TypeScript's purpose | |
| Inline styles for non-dynamic values | Inconsistent, hard to maintain | CSS modules, Tailwind, or styled-components |
| Memoizing everything | Adds complexity, often slower | Profile first, memoize second |
mcp__context7__resolve-library-idmcp__context7__query-docsreactnext.jstypescripttailwindcssvitest| Skill | Relationship |
|---|---|
| Strategy defines frontend test frameworks |
| Components are built with TDD cycle |
| Detailed React patterns complement this skill |
| Frontend performance follows optimization methodology |
| Review verifies component architecture and test coverage |
| Code quality principles apply to component code |
| Playwright E2E tests use this skill's page structure |
| UI acceptance criteria drive component tests |
anyunknown