Frontend Patterns

by affaan-mef648e01899bMIT275K starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated 3 days ago

Frontend development patterns for React, Next.js, state management, performance optimization, and UI best practices. Use when building or reviewing React or Next.js components, state, or render performance.

Instructions onlySoftware Development
AI-generated overview

Reference patterns for building React and Next.js frontends, covering components, hooks, state, performance, forms, and accessibility.

What it does
This skill supplies a reference guide of modern frontend development patterns for React and Next.js. It presents code examples for component composition, compound components, render props, custom hooks, state management with Context and reducers, memoization, code splitting, list virtualization, form validation, error boundaries, animation, and keyboard and focus accessibility. It also states privacy and data-handling boundaries for frontend examples. It produces guidance and example code rather than generated files.
When to use it
Use it when building or reviewing React or Next.js components, state logic, data fetching, or render performance. It also fits work on forms, client-side routing, and accessible, responsive UI patterns.
Requirements
No scripts are shipped; it is instructions only. The examples reference libraries such as React, Next.js, Zustand, SWR, React Query, Zod, TanStack Virtual, and Framer Motion, but nothing is required to read the guidance.

Frontend Development Patterns

Modern frontend patterns for React, Next.js, and performant user interfaces.

When to Activate

  • Building React components (composition, props, rendering)
  • Managing state (useState, useReducer, Zustand, Context)
  • Implementing data fetching (SWR, React Query, server components)
  • Optimizing performance (memoization, virtualization, code splitting)
  • Working with forms (validation, controlled inputs, Zod schemas)
  • Handling client-side routing and navigation
  • Building accessible, responsive UI patterns

Privacy and Data Boundaries

Frontend examples should use synthetic or domain-generic data. Do not collect, log, persist, or display credentials, access tokens, SSNs, health data, payment details, private emails, phone numbers, or other sensitive personal data unless the user explicitly requests a scoped implementation with appropriate validation, redaction, and access controls.

Avoid adding analytics, tracking pixels, third-party scripts, or external data sinks without explicit approval. When handling user data, prefer least-privilege APIs, client-side redaction before logging, and server-side validation for every boundary.

Component Patterns

Composition Over Inheritance

typescript
// PASS: GOOD: Component compositioninterface CardProps {  children: React.ReactNode  variant?: 'default' | 'outlined'}
export function Card({ children, variant = 'default' }: CardProps) {  return <div className={`card card-${variant}`}>{children}</div>}
export function CardHeader({ children }: { children: React.ReactNode }) {  return <div className="card-header">{children}</div>}
export function CardBody({ children }: { children: React.ReactNode }) {  return <div className="card-body">{children}</div>}
// Usage<Card>  <CardHeader>Title</CardHeader>  <CardBody>Content</CardBody></Card>

Compound Components

typescript
interface TabsContextValue {  activeTab: string  setActiveTab: (tab: string) => void}
const TabsContext = createContext<TabsContextValue | undefined>(undefined)
export function Tabs({ children, defaultTab }: {  children: React.ReactNode  defaultTab: string}) {  const [activeTab, setActiveTab] = useState(defaultTab)
  return (    <TabsContext.Provider value={{ activeTab, setActiveTab }}>      {children}    </TabsContext.Provider>  )}
export function TabList({ children }: { children: React.ReactNode }) {  return <div className="tab-list">{children}</div>}
export function Tab({ id, children }: { id: string, children: React.ReactNode }) {  const context = useContext(TabsContext)  if (!context) throw new Error('Tab must be used within Tabs')
  return (    <button      className={context.activeTab === id ? 'active' : ''}      onClick={() => context.setActiveTab(id)}    >      {children}    </button>  )}
// Usage<Tabs defaultTab="overview">  <TabList>    <Tab id="overview">Overview</Tab>    <Tab id="details">Details</Tab>  </TabList></Tabs>

Render Props Pattern

typescript
interface DataLoaderProps<T> {  url: string  children: (data: T | null, loading: boolean, error: Error | null) => React.ReactNode}
export function DataLoader<T>({ url, children }: DataLoaderProps<T>) {  const [data, setData] = useState<T | null>(null)  const [loading, setLoading] = useState(true)  const [error, setError] = useState<Error | null>(null)
  useEffect(() => {    fetch(url)      .then(res => res.json())      .then(setData)      .catch(setError)      .finally(() => setLoading(false))  }, [url])
  return <>{children(data, loading, error)}</>}
// Usage<DataLoader<Market[]> url="/api/markets">  {(markets, loading, error) => {    if (loading) return <Spinner />    if (error) return <Error error={error} />    return <MarketList markets={markets!} />  }}</DataLoader>

Custom Hooks Patterns

State Management Hook

typescript
export function useToggle(initialValue = false): [boolean, () => void] {  const [value, setValue] = useState(initialValue)
  const toggle = useCallback(() => {    setValue(v => !v)  }, [])
  return [value, toggle]}
// Usageconst [isOpen, toggleOpen] = useToggle()

Async Data Fetching Hook

typescript
interface UseQueryOptions<T> {  onSuccess?: (data: T) => void  onError?: (error: Error) => void  enabled?: boolean}
export function useQuery<T>(  key: string,  fetcher: () => Promise<T>,  options?: UseQueryOptions<T>) {  const [data, setData] = useState<T | null>(null)  const [error, setError] = useState<Error | null>(null)  const [loading, setLoading] = useState(false)
  // Keep the latest fetcher/options in refs so refetch stays referentially  // stable even when callers pass inline functions and object literals.  // Without this, every render creates a new refetch, and the effect below  // re-runs after each state update - an infinite fetch loop.  const fetcherRef = useRef(fetcher)  const optionsRef = useRef(options)  useEffect(() => {    fetcherRef.current = fetcher    optionsRef.current = options  })
  const refetch = useCallback(async () => {    setLoading(true)    setError(null)
    try {      const result = await fetcherRef.current()      setData(result)      optionsRef.current?.onSuccess?.(result)    } catch (err) {      const error = err as Error      setError(error)      optionsRef.current?.onError?.(error)    } finally {      setLoading(false)    }  }, [])
  const enabled = options?.enabled !== false
  useEffect(() => {    if (enabled) {      refetch()    }  }, [key, enabled, refetch])
  return { data, error, loading, refetch }}
// Usageconst { data: markets, loading, error, refetch } = useQuery(  'markets',  () => fetch('/api/markets').then(r => r.json()),  {    onSuccess: data => console.log('Fetched', data.length, 'markets'),    onError: err => console.error('Failed:', err)  })

Debounce Hook

typescript
export function useDebounce<T>(value: T, delay: number): T {  const [debouncedValue, setDebouncedValue] = useState<T>(value)
  useEffect(() => {    const handler = setTimeout(() => {      setDebouncedValue(value)    }, delay)
    return () => clearTimeout(handler)  }, [value, delay])
  return debouncedValue}
// Usageconst [searchQuery, setSearchQuery] = useState('')const debouncedQuery = useDebounce(searchQuery, 500)
useEffect(() => {  if (debouncedQuery) {    performSearch(debouncedQuery)  }}, [debouncedQuery])

State Management Patterns

Context + Reducer Pattern

typescript
interface State {  markets: Market[]  selectedMarket: Market | null  loading: boolean}
type Action =  | { type: 'SET_MARKETS'; payload: Market[] }  | { type: 'SELECT_MARKET'; payload: Market }  | { type: 'SET_LOADING'; payload: boolean }
function reducer(state: State, action: Action): State {  switch (action.type) {    case 'SET_MARKETS':      return { ...state, markets: action.payload }    case 'SELECT_MARKET':      return { ...state, selectedMarket: action.payload }    case 'SET_LOADING':      return { ...state, loading: action.payload }    default:      return state  }}
const MarketContext = createContext<{  state: State  dispatch: Dispatch<Action>} | undefined>(undefined)
export function MarketProvider({ children }: { children: React.ReactNode }) {  const [state, dispatch] = useReducer(reducer, {    markets: [],    selectedMarket: null,    loading: false  })
  return (    <MarketContext.Provider value={{ state, dispatch }}>      {children}    </MarketContext.Provider>  )}
export function useMarkets() {  const context = useContext(MarketContext)  if (!context) throw new Error('useMarkets must be used within MarketProvider')  return context}

Performance Optimization

Memoization

typescript
// PASS: useMemo for expensive computations// Copy before sorting - Array.prototype.sort mutates in placeconst sortedMarkets = useMemo(() => {  return [...markets].sort((a, b) => b.volume - a.volume)}, [markets])
// PASS: useCallback for functions passed to childrenconst handleSearch = useCallback((query: string) => {  setSearchQuery(query)}, [])
// PASS: React.memo for pure componentsexport const MarketCard = React.memo<MarketCardProps>(({ market }) => {  return (    <div className="market-card">      <h3>{market.name}</h3>      <p>{market.description}</p>    </div>  )})

Code Splitting & Lazy Loading

typescript
import { lazy, Suspense } from 'react'
// PASS: Lazy load heavy componentsconst HeavyChart = lazy(() => import('./HeavyChart'))const ThreeJsBackground = lazy(() => import('./ThreeJsBackground'))
export function Dashboard() {  return (    <div>      <Suspense fallback={<ChartSkeleton />}>        <HeavyChart data={data} />      </Suspense>
      <Suspense fallback={null}>        <ThreeJsBackground />      </Suspense>    </div>  )}

Virtualization for Long Lists

typescript
import { useVirtualizer } from '@tanstack/react-virtual'
export function VirtualMarketList({ markets }: { markets: Market[] }) {  const parentRef = useRef<HTMLDivElement>(null)
  const virtualizer = useVirtualizer({    count: markets.length,    getScrollElement: () => parentRef.current,    estimateSize: () => 100,  // Estimated row height    overscan: 5  // Extra items to render  })
  return (    <div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>      <div        style={{          height: `${virtualizer.getTotalSize()}px`,          position: 'relative'        }}      >        {virtualizer.getVirtualItems().map(virtualRow => (          <div            key={virtualRow.index}            style={{              position: 'absolute',              top: 0,              left: 0,              width: '100%',              height: `${virtualRow.size}px`,              transform: `translateY(${virtualRow.start}px)`            }}          >            <MarketCard market={markets[virtualRow.index]} />          </div>        ))}      </div>    </div>  )}

Form Handling Patterns

Controlled Form with Validation

typescript
interface FormData {  name: string  description: string  endDate: string}
interface FormErrors {  name?: string  description?: string  endDate?: string}
export function CreateMarketForm() {  const [formData, setFormData] = useState<FormData>({    name: '',    description: '',    endDate: ''  })
  const [errors, setErrors] = useState<FormErrors>({})
  const validate = (): boolean => {    const newErrors: FormErrors = {}
    if (!formData.name.trim()) {      newErrors.name = 'Name is required'    } else if (formData.name.length > 200) {      newErrors.name = 'Name must be under 200 characters'    }
    if (!formData.description.trim()) {      newErrors.description = 'Description is required'    }
    if (!formData.endDate) {      newErrors.endDate = 'End date is required'    }
    setErrors(newErrors)    return Object.keys(newErrors).length === 0  }
  const handleSubmit = async (e: React.FormEvent) => {    e.preventDefault()
    if (!validate()) return
    try {      await createMarket(formData)      // Success handling    } catch (error) {      // Error handling    }  }
  return (    <form onSubmit={handleSubmit}>      <input        value={formData.name}        onChange={e => setFormData(prev => ({ ...prev, name: e.target.value }))}        placeholder="Market name"      />      {errors.name && <span className="error">{errors.name}</span>}
      {/* Other fields */}
      <button type="submit">Create Market</button>    </form>  )}

Error Boundary Pattern

typescript
interface ErrorBoundaryState {  hasError: boolean  error: Error | null}
export class ErrorBoundary extends React.Component<  { children: React.ReactNode },  ErrorBoundaryState> {  state: ErrorBoundaryState = {    hasError: false,    error: null  }
  static getDerivedStateFromError(error: Error): ErrorBoundaryState {    return { hasError: true, error }  }
  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {    console.error('Error boundary caught:', error, errorInfo)  }
  render() {    if (this.state.hasError) {      return (        <div className="error-fallback">          <h2>Something went wrong</h2>          <p>{this.state.error?.message}</p>          <button onClick={() => this.setState({ hasError: false })}>            Try again          </button>        </div>      )    }
    return this.props.children  }}
// Usage<ErrorBoundary>  <App /></ErrorBoundary>

Animation Patterns

Framer Motion Animations

typescript
import { motion, AnimatePresence } from 'framer-motion'
// PASS: List animationsexport function AnimatedMarketList({ markets }: { markets: Market[] }) {  return (    <AnimatePresence>      {markets.map(market => (        <motion.div          key={market.id}          initial={{ opacity: 0, y: 20 }}          animate={{ opacity: 1, y: 0 }}          exit={{ opacity: 0, y: -20 }}          transition={{ duration: 0.3 }}        >          <MarketCard market={market} />        </motion.div>      ))}    </AnimatePresence>  )}
// PASS: Modal animationsexport function Modal({ isOpen, onClose, children }: ModalProps) {  return (    <AnimatePresence>      {isOpen && (        <>          <motion.div            className="modal-overlay"            initial={{ opacity: 0 }}            animate={{ opacity: 1 }}            exit={{ opacity: 0 }}            onClick={onClose}          />          <motion.div            className="modal-content"            initial={{ opacity: 0, scale: 0.9, y: 20 }}            animate={{ opacity: 1, scale: 1, y: 0 }}            exit={{ opacity: 0, scale: 0.9, y: 20 }}          >            {children}          </motion.div>        </>      )}    </AnimatePresence>  )}

Accessibility Patterns

Keyboard Navigation

typescript
export function Dropdown({ options, onSelect }: DropdownProps) {  const [isOpen, setIsOpen] = useState(false)  const [activeIndex, setActiveIndex] = useState(0)
  const handleKeyDown = (e: React.KeyboardEvent) => {    switch (e.key) {      case 'ArrowDown':        e.preventDefault()        setActiveIndex(i => Math.min(i + 1, options.length - 1))        break      case 'ArrowUp':        e.preventDefault()        setActiveIndex(i => Math.max(i - 1, 0))        break      case 'Enter':        e.preventDefault()        onSelect(options[activeIndex])        setIsOpen(false)        break      case 'Escape':        setIsOpen(false)        break    }  }
  return (    <div      role="combobox"      aria-expanded={isOpen}      aria-haspopup="listbox"      onKeyDown={handleKeyDown}    >      {/* Dropdown implementation */}    </div>  )}

Focus Management

typescript
export function Modal({ isOpen, onClose, children }: ModalProps) {  const modalRef = useRef<HTMLDivElement>(null)  const previousFocusRef = useRef<HTMLElement | null>(null)
  useEffect(() => {    if (isOpen) {      // Save currently focused element      previousFocusRef.current = document.activeElement as HTMLElement
      // Focus modal      modalRef.current?.focus()    } else {      // Restore focus when closing      previousFocusRef.current?.focus()    }  }, [isOpen])
  return isOpen ? (    <div      ref={modalRef}      role="dialog"      aria-modal="true"      tabIndex={-1}      onKeyDown={e => e.key === 'Escape' && onClose()}    >      {children}    </div>  ) : null}

Remember: Modern frontend patterns enable maintainable, performant user interfaces. Choose patterns that fit your project complexity.

Source and attribution

Source:affaan-m/eccin.agents/skills/frontend-patternsat commitef648e0

License: MIT

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal