Frontend Patterns

Pattern

Component State

Manage data that only affects a single component internally.

State Management Intermediate

Component State

Problem

Local UI state like toggle visibility, form input values, or hover states gets lifted into global stores or prop-drilled through multiple layers, creating unnecessary complexity. Simple interactions become tangled with application-wide state management, making components harder to reuse and test.

Solution

Declare component-specific data that changes over time and triggers re-renders when updated. This keeps UI synchronized with user interactions and makes components responsive to input without spreading state across the application.

Example

This example demonstrates a Web Component that manages its own internal toggle state and re-renders when the state changes.

// Web Component with self-managed state
class ToggleButton extends HTMLElement {
  constructor() {
    super();
    // Initialize component state - starts in "off" position
    this.isOn = false;
  }

  connectedCallback() {
    this.render();
    // Set up click handler to toggle state
    this.querySelector('button').addEventListener('click', () => {
      // Toggle the state between true and false
      this.isOn = !this.isOn;
      // Re-render to reflect the new state
      this.render();
    });
  }

  render() {
    // Display button label based on current state
    this.innerHTML = `<button>${this.isOn ? 'ON' : 'OFF'}</button>`;
  }
}

// Register the custom element
customElements.define('toggle-button', ToggleButton);

Benefits

  • Keeps UI state local and encapsulated within components.
  • Simplifies component logic by avoiding global state for local concerns.
  • Makes components more reusable since they manage their own state.
  • Easier to test components in isolation without external dependencies.
  • Automatically triggers re-renders when state changes.

Tradeoffs

  • Can lead to duplicated state if not carefully managed.
  • State is lost when components unmount unless persisted elsewhere.
  • Difficult to share state between sibling components without lifting.
  • May cause unnecessary re-renders if state updates trigger cascading effects.
  • Can be tempting to over-use, leading to state management complexity.
Stay Updated

Get New Patterns
in Your Inbox

Join thousands of developers receiving weekly insights on frontend architecture patterns

No spam. Unsubscribe anytime.