Free

2026-09-11

leptossignalsstate-machinesarchitecture

Why Leptos signals map cleanly onto headless component state machines

Leptos signals offer a compelling advantage over React's useState for building headless components: they map directly to state machine semantics with minimal boilerplate.

# Signal semantics vs React state

React's useState is designed around component lifecycle and re-rendering triggers. Leptos signals, by contrast, are designed around state mutation and subscription.

signals vs useState
// React: imperative state updates
const [isOpen, setIsOpen] = useState(false);
 
const handleOpen = () => setIsOpen(true);
const handleClose = () => setIsOpen(false);
 
// Leptos: declarative signal mutations
let is_open = create_signal(false);
 
let handle_open = move || {
  is_open.set(true);
};
 
let handle_close = move || {
  is_open.set(false);
};

# State machine patterns

The key insight is that Leptos signals naturally align with state machine transitions:

  1. State representation: Each signal represents a state variable
  2. Transitions: Signal mutations become state transitions
  3. Side effects: Computed values handle derived state
  4. Lifecycle: Effects handle enter/exit logic
dialog_state.rs
// State machine implementation
#[derive(Clone, Copy, PartialEq)]
enum DialogState {
  Closed,
  Opening,
  Open,
  Closing,
}
 
impl DialogState {
  fn transition(self, action: DialogAction) -> Self {
    match (self, action) {
      (DialogState::Closed, DialogAction::Open) => DialogState::Opening,
      (DialogState::Opening, _) => DialogState::Open,
      (DialogState::Open, DialogAction::Close) => DialogState::Closing,
      (DialogState::Closing, _) => DialogState::Closed,
      _ => self,
    }
  }
}

# Performance characteristics

The signal-based approach offers several benefits:

  • Granular updates: Only components that depend on specific signals re-render
  • No re-renders: State changes don't trigger re-renders unless subscribed
  • Predictable: Signal mutations are synchronous and predictable

This makes Leptos particularly well-suited for complex headless components where React might struggle with unnecessary re-renders.