← Back to list

react
by onmyway133
My favorite Claude Code tools
⭐ 3🍴 0📅 Jan 12, 2026
SKILL.md
name: "React" description: "React and TypeScript development with Tailwind, shadcn/ui, and modern patterns"
React
You are a React and TypeScript expert. Apply these guidelines when working on React projects.
Tech Stack
This skill assumes:
- TypeScript for type safety
- React 18+ with functional components
- Tailwind CSS for styling
- shadcn/ui for components
- pnpm as package manager
Project Structure
src/
├── app/ # Next.js app router pages
├── components/
│ ├── ui/ # shadcn/ui components
│ └── features/ # Feature-specific components
├── hooks/ # Custom hooks
├── lib/ # Utilities (cn, api clients)
├── types/ # Shared TypeScript types
└── services/ # API and data fetching
TypeScript Patterns
Type Definitions
// Prefer interfaces for extendable objects
interface User {
id: string;
name: string;
email: string;
}
// Use type for unions and computed types
type Status = 'idle' | 'loading' | 'success' | 'error';
type UserWithRole = User & { role: Role };
// Props interfaces above components
interface ButtonProps {
variant?: 'default' | 'destructive' | 'outline';
size?: 'sm' | 'md' | 'lg';
disabled?: boolean;
onClick?: () => void;
children: React.ReactNode;
}
Generics
// Reusable hooks with generics
function useLocalStorage<T>(key: string, initialValue: T) {
const [value, setValue] = useState<T>(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
});
return [value, setValue] as const;
}
// Constrained generics
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
Event Handlers
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setValue(e.target.value);
};
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
onSubmit(formData);
};
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
onClick?.();
};
Component Patterns
Basic Structure
interface CardProps {
title: string;
description?: string;
children: React.ReactNode;
className?: string;
}
export function Card({
title,
description,
children,
className
}: CardProps) {
return (
<div className={cn(
"rounded-lg border bg-card p-6 shadow-sm",
className
)}>
<h3 className="text-lg font-semibold">{title}</h3>
{description && (
<p className="mt-1 text-sm text-muted-foreground">
{description}
</p>
)}
<div className="mt-4">{children}</div>
</div>
);
}
Using shadcn/ui
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
export function CreateItemDialog() {
const [open, setOpen] = useState(false);
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button>Create Item</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Create New Item</DialogTitle>
</DialogHeader>
<form onSubmit={handleSubmit}>
<Input placeholder="Item name" />
<Button type="submit" className="mt-4">
Create
</Button>
</form>
</DialogContent>
</Dialog>
);
}
Tailwind Patterns
The cn() Utility
// lib/utils.ts
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
Conditional Classes
<div
className={cn(
"rounded-lg border p-4 transition-colors",
isActive && "border-primary bg-primary/5",
isDisabled && "cursor-not-allowed opacity-50",
className
)}
>
{children}
</div>
Responsive Design
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
{items.map((item) => (
<ItemCard key={item.id} item={item} />
))}
</div>
Common Patterns
// Centering
<div className="flex min-h-screen items-center justify-center">
// Card container
<div className="rounded-lg border bg-card p-6 shadow-sm">
// Flex with gap
<div className="flex items-center gap-2">
// Truncated text
<p className="truncate text-sm text-muted-foreground">
// Hover states
<button className="hover:bg-accent hover:text-accent-foreground">
Hooks
State Management
// useState with explicit type when needed
const [user, setUser] = useState<User | null>(null);
const [items, setItems] = useState<Item[]>([]);
// useReducer for complex state
type Action =
| { type: 'SET_LOADING'; payload: boolean }
| { type: 'SET_DATA'; payload: Data }
| { type: 'SET_ERROR'; payload: Error };
const [state, dispatch] = useReducer(reducer, initialState);
Effects
// With cleanup
useEffect(() => {
const controller = new AbortController();
fetchData(controller.signal)
.then(setData)
.catch((error) => {
if (!controller.signal.aborted) {
setError(error);
}
});
return () => controller.abort();
}, [dependency]);
Custom Hooks
function useDebounce<T>(value: T, delay: number): T {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const timer = setTimeout(() => setDebouncedValue(value), delay);
return () => clearTimeout(timer);
}, [value, delay]);
return debouncedValue;
}
function useToggle(initial = false) {
const [value, setValue] = useState(initial);
const toggle = useCallback(() => setValue((v) => !v), []);
const setTrue = useCallback(() => setValue(true), []);
const setFalse = useCallback(() => setValue(false), []);
return { value, toggle, setTrue, setFalse };
}
Data Fetching
Server Components (Next.js)
// Server Component - no 'use client' directive
async function UserList() {
const users = await fetchUsers();
return (
<ul className="space-y-2">
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
Client-Side Fetching
'use client';
function useUsers() {
const [users, setUsers] = useState<User[]>([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
const controller = new AbortController();
async function load() {
try {
const data = await fetchUsers(controller.signal);
setUsers(data);
} catch (e) {
if (!controller.signal.aborted) {
setError(e as Error);
}
} finally {
setIsLoading(false);
}
}
load();
return () => controller.abort();
}, []);
return { users, isLoading, error };
}
Forms
With React Hook Form
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
const schema = z.object({
email: z.string().email("Invalid email"),
password: z.string().min(8, "Password must be at least 8 characters"),
});
type FormData = z.infer<typeof schema>;
export function LoginForm() {
const {
register,
handleSubmit,
formState: { errors, isSubmitting },
} = useForm<FormData>({
resolver: zodResolver(schema),
});
const onSubmit = async (data: FormData) => {
await login(data);
};
return (
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
<div>
<Input {...register("email")} placeholder="Email" />
{errors.email && (
<p className="mt-1 text-sm text-destructive">
{errors.email.message}
</p>
)}
</div>
<div>
<Input
{...register("password")}
type="password"
placeholder="Password"
/>
{errors.password && (
<p className="mt-1 text-sm text-destructive">
{errors.password.message}
</p>
)}
</div>
<Button type="submit" disabled={isSubmitting}>
{isSubmitting ? "Signing in..." : "Sign In"}
</Button>
</form>
);
}
Naming Conventions
Files and Folders
- Components:
kebab-case.tsxorPascalCase.tsx - Hooks:
use-hook-name.ts - Utilities:
kebab-case.ts - Types:
types.tsortype-name.ts
Code
// Components: PascalCase
function UserProfile() {}
// Hooks: camelCase with 'use' prefix
function useAuth() {}
// Constants: SCREAMING_SNAKE_CASE
const API_BASE_URL = '/api';
const MAX_RETRY_COUNT = 3;
// Event handlers: handle + Event
const handleClick = () => {};
const handleSubmit = () => {};
const handleInputChange = () => {};
// Boolean variables: is/has/can/should prefix
const isLoading = true;
const hasError = false;
const canSubmit = true;
Error Handling
Error Boundaries
'use client';
interface ErrorBoundaryProps {
children: React.ReactNode;
fallback: React.ReactNode;
}
class ErrorBoundary extends Component<ErrorBoundaryProps, { hasError: boolean }> {
state = { hasError: false };
static getDerivedStateFromError() {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return this.props.fallback;
}
return this.props.children;
}
}
API Error Handling
class ApiError extends Error {
constructor(
message: string,
public statusCode: number,
public code?: string
) {
super(message);
this.name = 'ApiError';
}
}
async function fetchWithError<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) {
throw new ApiError(
'Request failed',
response.status,
response.statusText
);
}
return response.json();
}
Performance
Memoization
// Memoize expensive computations
const sortedItems = useMemo(
() => items.sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
// Memoize callbacks passed to children
const handleDelete = useCallback((id: string) => {
setItems((prev) => prev.filter((item) => item.id !== id));
}, []);
// Memoize components that render often
const MemoizedRow = memo(function Row({ item }: { item: Item }) {
return <div>{item.name}</div>;
});
Code Splitting
// Lazy load heavy components
const HeavyChart = lazy(() => import('./HeavyChart'));
function Dashboard() {
return (
<Suspense fallback={<Skeleton className="h-64 w-full" />}>
<HeavyChart data={data} />
</Suspense>
);
}
Common Commands
# Create new Next.js project
pnpm create next-app@latest my-app --typescript --tailwind --eslint
# Add shadcn/ui
pnpm dlx shadcn@latest init
# Add shadcn components
pnpm dlx shadcn@latest add button input dialog
# Add dependencies
pnpm add zod react-hook-form @hookform/resolvers
# Development
pnpm dev
# Build
pnpm build
Common Pitfalls
- Missing keys - Always provide stable, unique keys in lists
- Stale closures - Include all dependencies in useEffect/useCallback
- Prop drilling - Use context or composition for deep props
- Over-memoization - Profile before optimizing
- Type assertions - Prefer type guards over
ascasting - Missing 'use client' - Add directive for client-only features
- Inline objects in JSX - Creates new references on every render
Score
Total Score
60/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
✓LICENSE
ライセンスが設定されている
+10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
✓最近の活動
3ヶ月以内に更新
+5
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
○言語
プログラミング言語が設定されている
0/5
✓タグ
1つ以上のタグが設定されている
+5
Reviews
💬
Reviews coming soon

