internal/skills/content/react/SKILL.md
React 18+ framework guardrails, patterns, and best practices for AI-assisted development. Use when working with React projects (.jsx/.tsx files), or when the user mentions React. Provides component patterns, hooks, state management, testing, and performance guidelines.
npx skillsauth add ar4mirez/samuel reactInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Applies to: React 18+, TypeScript, Single Page Applications, Component Libraries, Web Apps
React.ReactNode for children props, not JSX.Elementuse prefix: useAuth, useQuery, useFetchuseEffect / useMemo / useCallback arraysuseEffect return functionuseReducer over useState for complex state logicreact-hooks plugin to enforce rulesuseState for simple, useReducer for complexReact.memo() only for expensive pure components (measure first)useMemo for expensive computations, useCallback for stable referencesReact.lazy() + SuspensePascalCase.tsx (e.g., UserCard.tsx)camelCase.ts with use prefix (e.g., useAuth.ts)camelCase.ts (e.g., formatDate.ts)camelCase.ts or types.ts per feature*.test.tsx or *.spec.tsx (co-located)*.module.css or *.styles.ts (co-located)my-app/
├── public/
│ └── index.html
├── src/
│ ├── assets/ # Static assets (images, fonts)
│ ├── components/ # Reusable components
│ │ ├── ui/ # Base UI components (Button, Input, Modal)
│ │ └── features/ # Feature-specific components
│ ├── hooks/ # Custom hooks
│ ├── contexts/ # React contexts
│ ├── services/ # API services
│ ├── utils/ # Utility functions
│ ├── types/ # TypeScript types
│ ├── pages/ # Page components (route-level)
│ ├── App.tsx
│ ├── main.tsx
│ └── index.css
├── tests/
├── package.json
├── tsconfig.json
├── vite.config.ts
└── README.md
components/ui/ for reusable design system primitivescomponents/features/ for domain-specific compositionshooks/ for shared custom hooks (feature-specific hooks co-locate with component)contexts/ for React context providersservices/ for API layer (fetch wrappers, API clients)pages/ for route-level components onlyinterface UserCardProps {
userId: string;
onSelect?: (user: User) => void;
className?: string;
}
export function UserCard({ userId, onSelect, className }: UserCardProps) {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchUser = async () => {
try {
const data = await userService.getById(userId);
setUser(data);
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]);
if (loading) return <Skeleton />;
if (!user) return null;
return (
<div className={cn('user-card', className)} onClick={() => onSelect?.(user)}>
<Avatar src={user.avatar} alt={user.name} />
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
);
}
interface CardProps {
title: string;
children: React.ReactNode;
footer?: React.ReactNode;
}
export function Card({ title, children, footer }: CardProps) {
return (
<div className="card">
<div className="card-header"><h2>{title}</h2></div>
<div className="card-body">{children}</div>
{footer && <div className="card-footer">{footer}</div>}
</div>
);
}
interface AuthContextValue {
user: User | null;
login: (credentials: LoginCredentials) => Promise<void>;
logout: () => void;
isAuthenticated: boolean;
}
const AuthContext = createContext<AuthContextValue | null>(null);
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const login = async (credentials: LoginCredentials) => {
const user = await authService.login(credentials);
setUser(user);
};
const logout = () => {
authService.logout();
setUser(null);
};
return (
<AuthContext.Provider value={{ user, login, logout, isAuthenticated: !!user }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
const context = useContext(AuthContext);
if (!context) throw new Error('useAuth must be used within AuthProvider');
return context;
}
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
function useUsers() {
return useQuery({
queryKey: ['users'],
queryFn: () => userService.getAll(),
staleTime: 5 * 60 * 1000,
});
}
function useCreateUser() {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (data: CreateUserDTO) => userService.create(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users'] });
},
});
}
import { create } from 'zustand';
interface CartStore {
items: CartItem[];
addItem: (item: CartItem) => void;
removeItem: (id: string) => void;
clearCart: () => void;
total: () => number;
}
const useCartStore = create<CartStore>()((set, get) => ({
items: [],
addItem: (item) => set((state) => ({ items: [...state.items, item] })),
removeItem: (id) => set((state) => ({
items: state.items.filter((item) => item.id !== id),
})),
clearCart: () => set({ items: [] }),
total: () => get().items.reduce((sum, item) => sum + item.price * item.quantity, 0),
}));
import { createBrowserRouter, RouterProvider } from 'react-router-dom';
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
const router = createBrowserRouter([
{
path: '/',
element: <Layout />,
children: [
{ index: true, element: <Home /> },
{ path: 'dashboard', element: <Dashboard /> },
{ path: 'settings', element: <Settings /> },
{ path: '*', element: <NotFound /> },
],
},
]);
function App() {
return (
<Suspense fallback={<LoadingSpinner />}>
<RouterProvider router={router} />
</Suspense>
);
}
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const userSchema = z.object({
email: z.string().email('Invalid email'),
password: z.string().min(8, 'Password must be at least 8 characters'),
name: z.string().min(2, 'Name is required'),
});
type UserFormData = z.infer<typeof userSchema>;
function UserForm({ onSubmit }: { onSubmit: (data: UserFormData) => void }) {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
reset,
} = useForm<UserFormData>({ resolver: zodResolver(userSchema) });
const handleFormSubmit = async (data: UserFormData) => {
await onSubmit(data);
reset();
};
return (
<form onSubmit={handleSubmit(handleFormSubmit)}>
<div>
<label htmlFor="email">Email</label>
<input id="email" type="email" {...register('email')} />
{errors.email && <span className="error">{errors.email.message}</span>}
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit'}
</button>
</form>
);
}
@testing-library/react) for component testsuserEvent over fireEvent for realistic user interactionsimport { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
describe('UserCard', () => {
it('renders user information', () => {
render(<UserCard user={mockUser} />);
expect(screen.getByText('John Doe')).toBeInTheDocument();
});
it('calls onSelect when clicked', async () => {
const onSelect = vi.fn();
render(<UserCard user={mockUser} onSelect={onSelect} />);
await userEvent.click(screen.getByRole('button'));
expect(onSelect).toHaveBeenCalledWith(mockUser);
});
});
npm create vite@latest my-app -- --template react-ts # New project
npm install # Install deps
npm run dev # Dev server
npm run build # Production build
npm run lint # ESLint
npm test # Run tests
npm run test:cov # Coverage
{
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.x",
"@tanstack/react-query": "^5.x",
"zustand": "^4.x",
"react-hook-form": "^7.x",
"zod": "^3.x",
"@hookform/resolvers": "^3.x"
},
"devDependencies": {
"@types/react": "^18.x",
"@types/react-dom": "^18.x",
"@testing-library/react": "^14.x",
"@testing-library/user-event": "^14.x",
"vitest": "^1.x",
"eslint-plugin-react-hooks": "^4.x"
}
}
{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react/recommended",
"plugin:react-hooks/recommended"
],
"rules": {
"react/react-in-jsx-scope": "off",
"react/prop-types": "off",
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn"
}
}
For detailed patterns and examples, see:
development
Zig language guardrails, patterns, and best practices for AI-assisted development. Use when working with Zig files (.zig), build.zig, or when the user mentions Zig. Provides comptime patterns, allocator conventions, C interop guidelines, and testing standards specific to this project's coding standards.
tools
WordPress framework guardrails, patterns, and best practices for AI-assisted development. Use when working with WordPress projects, or when the user mentions WordPress. Provides theme development, plugin architecture, REST API, blocks, and security guidelines.
tools
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs. Use when testing web apps, automating browser interactions, or debugging frontend issues.
tools
Suite of tools for creating elaborate, multi-component web applications using modern frontend technologies (React, Tailwind CSS, shadcn/ui). Use for complex projects requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX pages.