🧩

Composing React UI with Providers

Dec 4, 2025

Summary

  • Speaker examines complexity of large React UI components using Slack composer as example.
  • Argues against monolithic components with many boolean props and conditional logic.
  • Proposes composition plus lifted providers (context) to separate UI from state implementations.
  • Demonstrates composing header, input, footer, dropzone, and action components via JSX instead of arrays/flags.
  • Recommends lifting provider/state to allow external components to access actions without prop drilling.
  • Suggests provider interface decouples state implementation (useState, global sync hook) from component internals.
  • Emphasizes codebase design benefits for human developers and AI code generation/editing.

Action Items

  • (Soon – Team) Replace large boolean-prop monolith components with composed subcomponents and provider roots.
  • (Soon – Team) Create distinct action components for footer actions instead of a single actions array.
  • (Next Sprint – Team) Lift component provider state higher to allow external controls to access submit actions.
  • (Next Sprint – Team) Abstract common subtrees (e.g., common actions) as reusable compound components.
  • (Future – Team) Support multiple state implementations by swapping provider implementations (local vs global sync).

Composer Structure (Pattern)

  • Root Provider: exposes state, actions, meta (e.g., input ref) via context.
  • Frame/Header/Input/Footer: composed via JSX, each consumes provider context.
  • DropZone/File: added only when attachments supported (render or omit).
  • Actions: implemented as individual components, not an array of config objects.
  • Common Actions Component: wraps frequently used action components for reuse.
ComponentResponsibilityNotes
ProviderHolds state & shared actions; defines context interfaceImplementation can be local useState or global sync hook
FrameVisual container for composer UIRendered inside provider
HeaderComposer header UISimple composition unit
InputRenders and updates text from contextAgnostic to state implementation
FooterRenders action components and submit UIBuilt via JSX; accepts custom children
DropZoneHandles drag-and-drop attachmentsOnly render when attachments enabled
Common ActionsReusable group of left-side actionsWraps individual action components
External Actions RowActions outside composer box accessing submitUses provider context without prop drilling

Topic: Avoiding Boolean Prop Explosion

  • Problem: many boolean props lead to 30+ conditional checks across the component tree.
  • Symptoms: duplicated checks, prop drilling, fragile unions, and unreadable arrays of action configs.
  • Solution: use composition (JSX) to render distinct trees per use case, not flags controlling internals.

Topic: Using JSX Over Config Arrays

  • Implement every footer action as its own component; share base button internally.
  • Avoid looping over arrays with many conditional fields (menu, divider, items).
  • JSX enables clear, maintainable component trees and easy customization.

Topic: State Management Patterns

  • Provider/Context Interface: defines actions and state shape consumed by children.
  • Implementations:
    • Local/Ephemeral: provider uses useState for modal-forward composer.
    • Global/Synced: provider wraps a hook (e.g., useGlobalChannel) to sync across devices.
  • Key benefit: swap state implementation by replacing provider without changing children.

Topic: Lifting State For External Controls

  • Problem: buttons outside composer needing access to composer state or submit.
  • Pattern: lift provider higher so external components are still inside provider tree.
  • Benefit: external actions can call submit via context; no upward prop drilling or effect syncing.

Decisions

  • Favor composition and explicit JSX composition over single monolithic components with many props.
  • Use a provider-per-composer pattern; provider exposes a stable interface for children.
  • Implement shared subtrees (common actions) but allow per-composer custom action composition.
  • Swap state mechanisms by replacing provider implementation, not changing consumer components.

Open Questions

  • Which existing monolithic components should be prioritized for refactor first?
  • What testing strategy ensures provider swapping retains behavior across local/global state implementations?
  • Should a standard interface spec be documented for composer providers to ensure consistent consumption?
  • How to migrate existing code that currently relies on many boolean props without large regressions?