Claude-skill-registry frontend-react
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/frontend-react" ~/.claude/skills/majiayu000-claude-skill-registry-frontend-react && rm -rf "$T"
manifest:
skills/data/frontend-react/SKILL.mdsource content
React Frontend Stack
Live docs: Add
to prompt for up-to-date React, TanStack Query, Tailwind documentation.use context7
Quick Reference
| Topic | Reference |
|---|---|
| Components | components.md — Button, Input, Modal, patterns |
| State | state.md — useState, Zustand, Context, URL state |
Tooling (2025)
| Tool | Purpose | Why |
|---|---|---|
| Vite | Build tool | Fast HMR, ESM native |
| React 19 | UI library | RSC, Actions, use() |
| TypeScript | Type safety | Strict mode |
| Tailwind v4 | Styling | Utility-first, Vite plugin |
| TanStack Query | Data fetching | Caching, mutations |
| Zustand | State | Simple, no boilerplate |
| React Router 7 | Routing | Data loading, actions |
Notes:
- Tailwind v4: new config via Vite plugin, no
tailwind.config.js - TanStack Router may require React 18.3.1 (use
if needed)--legacy-peer-deps
Project Setup
pnpm create vite@latest my-app --template react-ts cd my-app pnpm add @tanstack/react-query zustand pnpm add -D tailwindcss @tailwindcss/vite
Vite Config
// vite.config.ts import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; import tailwindcss from '@tailwindcss/vite'; export default defineConfig({ plugins: [react(), tailwindcss()], resolve: { alias: { '@': '/src' }, }, });
Tailwind v4
/* src/index.css */ @import "tailwindcss";
Project Structure
src/ ├── main.tsx # Entry point ├── App.tsx # Root component ├── index.css # Tailwind imports ├── components/ │ ├── ui/ # Reusable UI (Button, Input, Card) │ └── features/ # Feature components ├── pages/ # Route components ├── hooks/ # Custom hooks ├── stores/ # Zustand stores ├── api/ # API client, queries ├── types/ # TypeScript types └── lib/ # Utilities
Component Patterns
Functional Component
interface UserCardProps { user: User; onEdit?: (id: string) => void; } export function UserCard({ user, onEdit }: UserCardProps) { return ( <div className="rounded-lg border p-4"> <h3 className="font-semibold">{user.name}</h3> <p className="text-sm text-gray-600">{user.email}</p> {onEdit && ( <button onClick={() => onEdit(user.id)} className="mt-2 text-blue-600 hover:underline" > Edit </button> )} </div> ); }
Component with Children
interface CardProps { title: string; children: React.ReactNode; className?: string; } export function Card({ title, children, className }: CardProps) { return ( <div className={cn("rounded-lg border bg-white p-6", className)}> <h2 className="mb-4 text-lg font-semibold">{title}</h2> {children} </div> ); }
Polymorphic Component
type ButtonProps<T extends React.ElementType = 'button'> = { as?: T; variant?: 'primary' | 'secondary'; children: React.ReactNode; } & React.ComponentPropsWithoutRef<T>; export function Button<T extends React.ElementType = 'button'>({ as, variant = 'primary', children, className, ...props }: ButtonProps<T>) { const Component = as || 'button'; return ( <Component className={cn( 'px-4 py-2 rounded-md font-medium', variant === 'primary' && 'bg-blue-600 text-white', variant === 'secondary' && 'bg-gray-200 text-gray-800', className )} {...props} > {children} </Component> ); } // Usage <Button>Click me</Button> <Button as="a" href="/about">Link</Button>
Hooks
Custom Hook Example
function useLocalStorage<T>(key: string, initialValue: T) { const [value, setValue] = useState<T>(() => { const stored = localStorage.getItem(key); return stored ? JSON.parse(stored) : initialValue; }); useEffect(() => { localStorage.setItem(key, JSON.stringify(value)); }, [key, value]); return [value, setValue] as const; }
useDebounce
function useDebounce<T>(value: T, delay: number): T { const [debounced, setDebounced] = useState(value); useEffect(() => { const timer = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(timer); }, [value, delay]); return debounced; }
TanStack Query
Setup
// main.tsx import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 60 * 1000, retry: 1, }, }, }); <QueryClientProvider client={queryClient}> <App /> </QueryClientProvider>
Query Hook
// api/users.ts import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; export function useUsers() { return useQuery({ queryKey: ['users'], queryFn: () => fetch('/api/users').then(r => r.json()), }); } export function useUser(id: string) { return useQuery({ queryKey: ['users', id], queryFn: () => fetch(`/api/users/${id}`).then(r => r.json()), enabled: !!id, }); } export function useCreateUser() { const queryClient = useQueryClient(); return useMutation({ mutationFn: (data: CreateUser) => fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }).then(r => r.json()), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['users'] }); }, }); }
Usage in Component
function UserList() { const { data: users, isLoading, error } = useUsers(); const createUser = useCreateUser(); if (isLoading) return <Spinner />; if (error) return <Error message={error.message} />; return ( <div> {users.map(user => <UserCard key={user.id} user={user} />)} <button onClick={() => createUser.mutate({ name: 'New' })}> Add User </button> </div> ); }
Zustand State
// stores/auth.ts import { create } from 'zustand'; import { persist } from 'zustand/middleware'; interface AuthState { user: User | null; token: string | null; login: (user: User, token: string) => void; logout: () => void; } export const useAuthStore = create<AuthState>()( persist( (set) => ({ user: null, token: null, login: (user, token) => set({ user, token }), logout: () => set({ user: null, token: null }), }), { name: 'auth-storage' } ) ); // Usage const { user, login, logout } = useAuthStore();
Forms
import { useState, FormEvent } from 'react'; function LoginForm() { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [errors, setErrors] = useState<Record<string, string>>({}); const handleSubmit = async (e: FormEvent) => { e.preventDefault(); setErrors({}); if (!email) { setErrors(prev => ({ ...prev, email: 'Required' })); return; } // Submit... }; return ( <form onSubmit={handleSubmit} className="space-y-4"> <div> <input type="email" value={email} onChange={e => setEmail(e.target.value)} className="w-full rounded border px-3 py-2" placeholder="Email" /> {errors.email && <p className="text-sm text-red-500">{errors.email}</p>} </div> <button type="submit" className="w-full rounded bg-blue-600 py-2 text-white"> Login </button> </form> ); }
Anti-patterns
| Don't | Do Instead |
|---|---|
| CRA (Create React App) | Vite |
| CSS Modules / styled-components | Tailwind |
| Redux (complex) | Zustand (simple) |
| useEffect for data fetching | TanStack Query |
| Prop drilling | Context or Zustand |
types | Proper TypeScript types |
| Index as key | Unique ID as key |
| Inline object props | useMemo or extract |