From qe-framework
Builds React 18+ components, custom hooks, and state management. Handles Server Components, Suspense, React 19 features, and performance optimization.
How this skill is triggered — by the user, by Claude, or both
Slash command
/qe-framework:Qreact-expertThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Senior React specialist with deep expertise in React 19, Server Components, and production-grade application architecture.
Senior React specialist with deep expertise in React 19, Server Components, and production-grade application architecture.
use()tsc --noEmit; if it fails, review reported errors, fix all type issues, and re-run until clean before proceedingLoad detailed guidance based on context:
| Topic | Reference | Load When |
|---|---|---|
| Server Components | references/server-components.md | RSC patterns, Next.js App Router |
| React 19 | references/react-19-features.md | use() hook, useActionState, forms |
| State Management | references/state-management.md | Context, Zustand, Redux, TanStack |
| Hooks | references/hooks-patterns.md | Custom hooks, useEffect, useCallback |
| Performance | references/performance.md | memo, lazy, virtualization |
| Testing | references/testing-react.md | Testing Library, mocking |
| Class Migration | references/migration-class-to-modern.md | Converting class components to hooks/RSC |
// app/users/page.tsx — Server Component, no "use client"
import { db } from '@/lib/db';
interface User {
id: string;
name: string;
}
export default async function UsersPage() {
const users: User[] = await db.user.findMany();
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
useActionState'use client';
import { useActionState } from 'react';
async function submitForm(_prev: string, formData: FormData): Promise<string> {
const name = formData.get('name') as string;
// perform server action or fetch
return `Hello, ${name}!`;
}
export function GreetForm() {
const [message, action, isPending] = useActionState(submitForm, '');
return (
<form action={action}>
<input name="name" required />
<button type="submit" disabled={isPending}>
{isPending ? 'Submitting…' : 'Submit'}
</button>
{message && <p>{message}</p>}
</form>
);
}
import { useState, useEffect } from 'react';
function useWindowWidth(): number {
const [width, setWidth] = useState(() => window.innerWidth);
useEffect(() => {
const handler = () => setWidth(window.innerWidth);
window.addEventListener('resize', handler);
return () => window.removeEventListener('resize', handler); // cleanup
}, []);
return width;
}
key props correctly (stable, unique identifiers)/**
* Button component with custom styling and click handler.
* @param props - Button component props
* @param props.label - Button text
* @param props.onClick - Click event handler
* @param props.disabled - Optional disabled state
* @returns JSX.Element - Rendered button
* @example
* <ActionButton label="Click me" onClick={() => alert('clicked')} />
*/
interface ActionButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
}
export function ActionButton({ label, onClick, disabled = false }: ActionButtonProps) {
return (
<button onClick={onClick} disabled={disabled}>
{label}
</button>
);
}
import { Component, ReactNode } from 'react';
/**
* ErrorBoundary class component for catching rendering errors.
* @param children - Child components to wrap
*/
class ErrorBoundary extends Component<{ children: ReactNode }, { hasError: boolean }> {
constructor(props: { children: ReactNode }) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) return <p>Error occurred</p>;
return this.props.children;
}
}
// React 19: useErrorBoundary hook (client component)
import { useErrorBoundary } from 'react';
export function SafeComponent() {
const { resetBoundary } = useErrorBoundary();
return <button onClick={resetBoundary}>Reset</button>;
}
import { useState, useCallback, useMemo } from 'react';
/**
* Custom hook for managing form state with validation.
* @template T - Form data type
* @param initialValue - Initial form state
* @param onSubmit - Form submission callback
* @returns [formState, handlers, isValid]
* @example
* const [form, { setValue }, isValid] = useForm({ name: '' }, handleSubmit);
*/
function useForm<T extends Record<string, any>>(
initialValue: T,
onSubmit: (data: T) => Promise<void>
) {
const [formState, setFormState] = useState(initialValue);
const [errors, setErrors] = useState<Partial<T>>({});
const setValue = useCallback((key: keyof T, value: T[keyof T]) => {
setFormState((prev) => ({ ...prev, [key]: value }));
}, []);
const isValid = useMemo(() => Object.keys(errors).length === 0, [errors]);
return [formState, { setValue, setErrors }, isValid] as const;
}
/**
* Displays a list of items with optional filtering.
* @param props - Component props
* @param props.items - Array of items to display
* @param props.onSelect - Callback when item is selected
* @returns JSX.Element - Rendered list component
* @example
* <ItemList items={data} onSelect={handleSelect} />
*/
/**
* Custom hook for managing toggle state.
* @param initialValue - Initial toggle state
* @returns [state, toggle, reset] - Current state, toggle function, and reset function
* @example
* const [isOpen, toggle] = useToggle(false);
*/
/**
* Validates email format.
* @param email - Email string to validate
* @returns boolean - True if valid email format
* @throws Error if email is null/undefined
* @example
* const valid = isValidEmail('[email protected]');
*/
Run before commit:
npx eslint {file} --ext .ts,.tsx — Check react-hooks/rules-of-hooks (error), react-hooks/exhaustive-deps (warn)npx tsc --noEmit — Type check all filesnpx prettier --write {file} — Format codeConfig files: .eslintrc.json, tsconfig.json, .prettierrc
Critical rules:
react-hooks/rules-of-hooks: Error — hooks must be in function bodyreact-hooks/exhaustive-deps: Warn — useEffect dependencies must be complete{...userInput}npm audit, use lock files, review dependenciesjavascript: and data: schemes; use URL() constructor1. Prop Drilling → Use Context or state management library
// WRONG: Drilling through many components
<ComponentA user={user}><ComponentB user={user}>...</ComponentB></ComponentA>
// CORRECT: Use Context
const UserContext = createContext<User | null>(null);
<UserContext.Provider value={user}><ComponentB /></UserContext.Provider>
2. useEffect for Derived State → Use useMemo
// WRONG: Recalculate on every render
const [fullName, setFullName] = useState('');
useEffect(() => setFullName(`${first} ${last}`), [first, last]);
// CORRECT: Memoize derived value
const fullName = useMemo(() => `${first} ${last}`, [first, last]);
3. Inline Components → Extract to named functions
// WRONG: Defined inside render, recreated each render
function Parent() { return <div><Child /></div>; }
const Child = () => <span>test</span>;
// CORRECT: Named component outside parent
const Child = () => <span>test</span>;
function Parent() { return <div><Child /></div>; }
4. Index as Key → Use stable unique IDs
// WRONG: Index changes with reordering
{items.map((item, idx) => <Item key={idx} />)}
// CORRECT: Use unique identifier
{items.map((item) => <Item key={item.id} />)}
5. Direct State Mutation → Use immutable updates
// WRONG: Mutates state directly
const copy = state;
copy.name = 'new';
setState(copy);
// CORRECT: Create new object
setState({ ...state, name: 'new' });
When implementing React features, provide:
React 19, Server Components, use() hook, Suspense, TypeScript, TanStack Query, Zustand, Redux Toolkit, React Router, React Testing Library, Vitest/Jest, Next.js App Router, accessibility (WCAG)
npx claudepluginhub inho-team/qe-framework --plugin qe-frameworkCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.