skills/stack/react-typescript-patterns/SKILL.md
# React TypeScript Patterns Patterns for building type-safe, maintainable React applications with TypeScript. Components should be small, well-typed, and composable. --- ## Component Typing Every component MUST have an explicit Props interface. Never use `any` or inline object types. ```tsx // YES — Explicit interface, well-documented interface InvoiceCardProps { invoice: Invoice; onEdit: (id: string) => void; onDelete: (id: string) => void; isSelected?: boolean; } function Invoice
npx skillsauth add 33prime/rtg-forge skills/stack/react-typescript-patternsInstall 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.
Patterns for building type-safe, maintainable React applications with TypeScript. Components should be small, well-typed, and composable.
Every component MUST have an explicit Props interface. Never use any or inline object types.
// YES — Explicit interface, well-documented
interface InvoiceCardProps {
invoice: Invoice;
onEdit: (id: string) => void;
onDelete: (id: string) => void;
isSelected?: boolean;
}
function InvoiceCard({ invoice, onEdit, onDelete, isSelected = false }: InvoiceCardProps) {
return (/* ... */);
}
// NO — Inline types, any, or no types
function InvoiceCard({ invoice, onEdit, onDelete, isSelected }: any) {
return (/* ... */);
}
<ComponentName>Props? modifier? with a sensible default in destructuring...rest without typing — use ComponentPropsWithoutRef<"div"> if neededUse the correct children typing for the component's purpose:
// Accepting any React children
interface CardProps {
children: React.ReactNode;
variant?: "default" | "outlined";
}
// Render prop pattern — children is a function
interface DataLoaderProps<T> {
query: () => Promise<T>;
children: (data: T) => React.ReactNode;
}
// Slot pattern — named children
interface LayoutProps {
sidebar: React.ReactNode;
header: React.ReactNode;
children: React.ReactNode;
}
| Pattern | Use When |
|---|---|
| React.ReactNode | Component wraps generic content |
| (data: T) => ReactNode | Component provides data/state to children |
| Named slots | Layout components with multiple content areas |
| No children | Leaf components that render their own content |
Extract reusable logic into custom hooks. Hooks encapsulate state, effects, and derived data.
// Custom hook for invoice operations
function useInvoice(invoiceId: string) {
const [invoice, setInvoice] = useState<Invoice | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
useEffect(() => {
let cancelled = false;
async function fetchInvoice() {
try {
setIsLoading(true);
const data = await invoiceApi.getById(invoiceId);
if (!cancelled) {
setInvoice(data);
setError(null);
}
} catch (err) {
if (!cancelled) {
setError(err instanceof Error ? err : new Error("Unknown error"));
}
} finally {
if (!cancelled) {
setIsLoading(false);
}
}
}
fetchInvoice();
return () => { cancelled = true; };
}, [invoiceId]);
return { invoice, isLoading, error } as const;
}
use — useInvoice, useAuth, useDebounceas constuseEffect return functionshooks/useInvoice.tsPrefer composition over configuration. Build complex UIs from small, focused components.
// YES — Composed from small, focused components
function InvoicePage() {
const { invoices, isLoading } = useInvoices();
if (isLoading) return <LoadingSpinner />;
return (
<PageLayout>
<PageHeader title="Invoices" action={<CreateInvoiceButton />} />
<InvoiceFilters />
<InvoiceList invoices={invoices} />
</PageLayout>
);
}
// NO — Monolithic component doing everything
function InvoicePage() {
// 200 lines of state, effects, handlers, and JSX...
}
Use TypeScript generics for reusable components:
interface SelectProps<T> {
options: T[];
value: T | null;
onChange: (value: T) => void;
getLabel: (item: T) => string;
getKey: (item: T) => string;
placeholder?: string;
}
function Select<T>({ options, value, onChange, getLabel, getKey, placeholder }: SelectProps<T>) {
return (
<select
value={value ? getKey(value) : ""}
onChange={(e) => {
const selected = options.find((opt) => getKey(opt) === e.target.value);
if (selected) onChange(selected);
}}
>
{placeholder && <option value="">{placeholder}</option>}
{options.map((opt) => (
<option key={getKey(opt)} value={getKey(opt)}>
{getLabel(opt)}
</option>
))}
</select>
);
}
// Usage is fully typed:
<Select
options={customers}
value={selectedCustomer}
onChange={setSelectedCustomer}
getLabel={(c) => c.name} // TypeScript knows c is Customer
getKey={(c) => c.id}
placeholder="Select a customer"
/>
Wrap async/fallible components with error boundaries:
import { Component, type ErrorInfo, type ReactNode } from "react";
interface ErrorBoundaryProps {
fallback: ReactNode | ((error: Error) => ReactNode);
children: ReactNode;
onError?: (error: Error, errorInfo: ErrorInfo) => void;
}
interface ErrorBoundaryState {
error: Error | null;
}
class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
state: ErrorBoundaryState = { error: null };
static getDerivedStateFromError(error: Error): ErrorBoundaryState {
return { error };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
this.props.onError?.(error, errorInfo);
}
render() {
if (this.state.error) {
const { fallback } = this.props;
return typeof fallback === "function" ? fallback(this.state.error) : fallback;
}
return this.props.children;
}
}
onErrorType event handlers explicitly:
// Typed handler function
const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
// ...
};
// Typed callback props
interface FormProps {
onSubmit: (data: FormData) => void; // Domain event
onCancel: () => void; // Simple action
onChange: (field: string, value: string) => void; // Field change
}
// BAD — Object literal in JSX creates new reference every render
<UserList style={{ marginTop: 16 }} config={{ showAvatar: true }} />
// GOOD — Stable references
const listStyle = { marginTop: 16 } as const;
const listConfig = { showAvatar: true } as const;
<UserList style={listStyle} config={listConfig} />
// BAD — Inline function creates new reference every render
<Button onClick={() => handleDelete(item.id)} />
// GOOD — useCallback for stable reference (when needed for memo'd children)
const handleDeleteItem = useCallback(() => handleDelete(item.id), [item.id]);
<Button onClick={handleDeleteItem} />
// BAD — useEffect for derived state
useEffect(() => {
setFullName(`${firstName} ${lastName}`);
}, [firstName, lastName]);
// GOOD — useMemo for derived values
const fullName = useMemo(() => `${firstName} ${lastName}`, [firstName, lastName]);
// Or even simpler — just compute it:
const fullName = `${firstName} ${lastName}`;
development
# Parallel Execution > This skill is under development. Workflow patterns for running independent tasks in parallel to improve performance and throughput. ## Topics to Cover - Identifying independent tasks suitable for parallel execution - `asyncio.gather()` with `return_exceptions=True` - `asyncio.TaskGroup` for structured concurrency (Python 3.11+) - Semaphores for bounded concurrency - `Promise.all()` and `Promise.allSettled()` in TypeScript - Handling partial failures (some tasks succeed
development
# Module Extraction > This skill is under development. Workflow for identifying and extracting reusable modules from existing codebases. Extract when a pattern is used in 3+ places and has stabilized. ## Topics to Cover - Identifying extraction candidates (rule of three) - Defining module boundaries and public interface - Dependency analysis: what does the module need? - Interface design: protocols, abstract base classes - Step-by-step extraction process - Testing strategy: tests before, dur
development
# Forge Orchestrate — Intelligent Build Orchestration You are a build planner, not a build executor. Your job is to look at a project, figure out what's left to build, decompose the work into parallel streams, assign the right intelligence level to each stream, estimate cost, and hand the user a set of terminal commands they can run. You plan. They execute. --- ## Stream Decomposition The unit of parallelism is a **stream** — a self-contained bundle of tasks that one Claude session handles e
development
# Code Review > This skill is under development. Workflow for conducting effective code reviews that catch real issues and improve code quality. ## Topics to Cover - Review priorities: correctness > design > performance > style - What to check in every review (checklist) - How to give constructive feedback - Automated checks that should run before human review - Review scope: how big is too big? - Patterns for reviewing database migrations - Patterns for reviewing API changes - When to reque