React component patterns, forms, accessibility, and responsive design. Use when building React components, handling forms, or ensuring accessibility...
Common pitfalls and correct patterns for React development.
Verify loading/error states and data checks.
Ensure Zod schemas and proper error display.
Verify ARIA labels and keyboard navigation.
// ā
Define helpers before use or as exports
function formatPrice(price: number) { ... }
export default function Component() {
// ā
Check data exists before accessing
if (!data) return <Loading />;
// ā
useEffect for side effects only
useEffect(() => {
fetchData();
}, []);
// ā
data-testid on interactive elements
return <button data-testid="submit-btn">Submit</button>;
}
// ā WRONG: Defining function in render
return <button onClick={() => {
function doSomething() { } // Don't define here
doSomething();
}}>
// ā
Navigation with router, not window
import { Link, useLocation } from 'wouter';
<Link to="/dashboard">Go</Link>
// ā window.location.href = '/dashboard'
// ā
Wrap major components in error boundaries
class ErrorBoundary extends React.Component {
state = { hasError: false, error: null };
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, info) {
logError({ error, componentStack: info.componentStack });
}
render() {
if (this.state.hasError) {
return <ErrorFallback onRetry={() => this.setState({ hasError: false })} />;
}
return this.props.children;
}
}
// ā
Graceful degradation
function Dashboard() {
const { data, error, isLoading } = useQuery(...);
if (isLoading) return <Skeleton />;
if (error) return <ErrorCard message="Unable to load" onRetry={refetch} />;
if (!data) return <EmptyState />;
return <DashboardContent data={data} />;
}
// ā
Zod schemas for all forms
const createStrategySchema = z.object({
name: z.string().min(1, 'Name required').max(100),
type: z.enum(['cross-exchange', 'triangular']),
minProfit: z.number().positive('Must be positive'),
});
// ā
React Hook Form with Zod
const form = useForm<z.infer<typeof createStrategySchema>>({
resolver: zodResolver(createStrategySchema),
});
// ā
Show errors inline
{errors.name && <span className="text-red-500">{errors.name.message}</span>}
// ā
Disable submit while validating/submitting
<button disabled={isSubmitting || !isValid}>Submit</button>
/* ā
Mobile-first breakpoints */
.container { padding: 1rem; }
@media (min-width: 768px) {
.container { padding: 2rem; }
}
/* ā
Touch-friendly button sizes (min 44px) */
.btn { min-height: 44px; min-width: 44px; }
/* ā
Horizontal scroll for data tables on mobile */
.table-container { overflow-x: auto; }
// ā
Semantic HTML
<nav>...</nav>
<main>...</main>
<button>Click me</button> // Not <div onClick>
// ā
ARIA labels
<button aria-label="Close dialog">Ć</button>
// ā
Keyboard navigation
<button onKeyDown={(e) => e.key === 'Enter' && handleClick()}>
// ā
Focus indicators
button:focus { outline: 2px solid blue; outline-offset: 2px; }