React Ui Patterns Engine
Battle-tested skill for modern, react, patterns, loading. Includes structured workflows, validation checks, and reusable patterns for development.
React UI Patterns Engine
A practical skill for building robust React user interfaces with proper loading states, error handling, optimistic updates, form management, and accessible interactive components. Focused on UI correctness rather than architecture.
When to Use This Skill
Choose this skill when:
- Building interactive UI components that handle loading, error, and empty states
- Implementing forms with validation, submission, and error recovery
- Creating accessible modals, dropdowns, tooltips, and popovers
- Adding toast notifications, skeleton loaders, and progressive disclosure
- Handling pagination, infinite scroll, and virtualized lists
Consider alternatives when:
- Designing application architecture ā use a React patterns skill
- Building a component library from scratch ā use a design system skill
- Working on data fetching strategy ā use a data fetching skill
- Optimizing bundle performance ā use a performance profiling skill
Quick Start
# Install common UI dependencies npm install @radix-ui/react-dialog @radix-ui/react-toast npm install react-hook-form zod @hookform/resolvers
// Robust async UI component with all states handled function UserList() { const { data, loading, error, retry } = useFetch<User[]>('/api/users'); if (loading) return <SkeletonList count={5} />; if (error) return <ErrorCard message={error.message} onRetry={retry} />; if (!data?.length) return <EmptyState icon="users" message="No users yet" />; return ( <ul role="list"> {data.map(user => ( <UserCard key={user.id} user={user} /> ))} </ul> ); }
Core Concepts
UI State Machine
| State | Visual | User Action | Transition |
|---|---|---|---|
idle | Default UI | Trigger action | ā loading |
loading | Skeleton/spinner | Cancel (optional) | ā success or error |
success | Data rendered | Interact | ā idle |
error | Error message + retry | Click retry | ā loading |
empty | Empty state illustration | Create new | ā loading |
Form Pattern with Validation
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 address'), password: z.string().min(8, 'Password must be at least 8 characters'), role: z.enum(['admin', 'editor', 'viewer']), }); type FormData = z.infer<typeof schema>; function CreateUserForm({ onSubmit }: { onSubmit: (data: FormData) => Promise<void> }) { const { register, handleSubmit, formState: { errors, isSubmitting }, setError, } = useForm<FormData>({ resolver: zodResolver(schema) }); const submit = async (data: FormData) => { try { await onSubmit(data); } catch (err: any) { setError('root', { message: err.message || 'Submission failed' }); } }; return ( <form onSubmit={handleSubmit(submit)} noValidate> <Field label="Email" error={errors.email?.message}> <input type="email" {...register('email')} aria-invalid={!!errors.email} /> </Field> <Field label="Password" error={errors.password?.message}> <input type="password" {...register('password')} aria-invalid={!!errors.password} /> </Field> {errors.root && <Alert variant="error">{errors.root.message}</Alert>} <button type="submit" disabled={isSubmitting}> {isSubmitting ? 'Creating...' : 'Create User'} </button> </form> ); }
Accessible Modal Pattern
function Modal({ open, onClose, title, children }: ModalProps) { const overlayRef = useRef<HTMLDivElement>(null); useEffect(() => { if (!open) return; const prev = document.activeElement as HTMLElement; document.body.style.overflow = 'hidden'; return () => { document.body.style.overflow = ''; prev?.focus(); }; }, [open]); if (!open) return null; return createPortal( <div ref={overlayRef} className="modal-overlay" onClick={e => e.target === overlayRef.current && onClose()} role="presentation"> <div role="dialog" aria-modal="true" aria-label={title}> <h2>{title}</h2> {children} <button onClick={onClose} aria-label="Close">Ć</button> </div> </div>, document.body ); }
Configuration
| Parameter | Type | Default | Description |
|---|---|---|---|
skeletonAnimation | string | 'pulse' | Animation style: pulse, wave, or none |
toastDuration | number | 5000 | Auto-dismiss time for toast notifications (ms) |
toastPosition | string | 'bottom-right' | Toast container position on screen |
formResetOnSuccess | boolean | true | Reset form fields after successful submission |
modalCloseOnOverlay | boolean | true | Close modal when clicking overlay backdrop |
infiniteScrollThreshold | number | 200 | Pixel distance from bottom to trigger next page load |
Best Practices
-
Never show stale UI without indication ā If cached data is displayed while fresh data loads, show a subtle indicator (dimmed content or top progress bar). Users should always know whether they're seeing current or cached information.
-
Always surface errors visibly ā Failed API calls must produce visible user feedback ā inline error messages, toast notifications, or error cards with retry buttons. Silent failures cause confusion and data loss.
-
Use skeleton screens instead of spinners for layout-stable content ā Skeletons preserve layout dimensions and reduce perceived loading time. Reserve spinners for actions where content dimensions are unknown.
-
Implement keyboard navigation for all interactive components ā Modals need focus trapping, dropdowns need arrow key navigation, custom selects need type-ahead search. Test with keyboard-only navigation before considering a component complete.
-
Debounce user inputs that trigger expensive operations ā Search inputs, filter changes, and resize handlers should be debounced (typically 300ms for search, 150ms for filters). This prevents unnecessary API calls and improves responsiveness.
Common Issues
Modal focus not returning after close ā When a modal opens, store a reference to document.activeElement. On close, call .focus() on that stored element. Without this, keyboard users lose their place in the page after every modal interaction.
Toast notifications stacking and overflowing ā Without a maximum count, rapid events can spawn dozens of toasts that obscure content. Limit visible toasts to 3-5, queue additional ones, and always provide a manual dismiss button alongside auto-dismiss timers.
Infinite scroll loading duplicates ā Race conditions between scroll events and API responses cause the same page to load multiple times. Use a loading flag ref (not state) to gate fetch calls, and deduplicate by ID before appending new items to the list.
Reviews
No reviews yet. Be the first to review this template!
Similar Templates
Full-Stack Code Reviewer
Comprehensive code review skill that checks for security vulnerabilities, performance issues, accessibility, and best practices across frontend and backend code.
Test Suite Generator
Generates comprehensive test suites with unit tests, integration tests, and edge cases. Supports Jest, Vitest, Pytest, and Go testing.
Pro Architecture Workspace
Battle-tested skill for architectural, decision, making, framework. Includes structured workflows, validation checks, and reusable patterns for development.