Manage complex workflows with state machines
Learn how to implement state machines using hyperscript for managing multi-step
processes, workflows, and complex UI states. Perfect for checkout flows, wizards, and game logic!
state managementworkflowstransitionsguards
Order Processing State Machine
This demo shows a complete order workflow with state transitions and guards:
State Flow
Cart
Add items to cart
↓
Checkout
Review order details
↓
Payment
Enter payment info
↓
Processing
Processing payment
↓
Completed
Order confirmed
Cancelled
Order cancelled
Current State: Shopping Cart
Click buttons to transition between states
Order Summary
Subtotal:$0.00
Shipping:$10.00
Tax:$12.00
Total:$151.99
State History
The Code
State Machine Initialization:
<div id="state-machine"_="init
set :state to 'idle'
set :data to {}
end
on updateUI
-- Update UI based on current state
for box in .state-box
remove .active from box
end
add .active to #state-{:state}
end">
</div>
State Transition with Guard:
<button _="on click
-- Guard: Check if transition is allowed
if :state is 'checkout' and :paymentValid
set :state to 'payment'
send updateUI to #state-machine
else
log 'Cannot transition: conditions not met'
end">
Next Step
</button>
State Transition Handler:
<div _="on checkout
if :state is 'cart'
set :state to 'checkout'
call :history.push({
from: 'cart',
to: 'checkout',
timestamp: Date.now()
})
send updateUI
end
end">
</div>
How it works:
:state - Local variable tracking current state
init - Initialization block for setting up state machine
on eventName - Custom event handlers for state transitions
send event to #element - Trigger state transitions
Guards: Conditional checks before allowing transitions
Key Concepts:
States: Discrete modes of the system (cart, checkout, payment, etc.)
Transitions: Allowed movements between states
Guards: Conditions that must be met for transitions
Actions: Side effects when entering/leaving states
Events: Triggers that cause state transitions
State Machine Pattern:
-- Define states
set :states to ['idle', 'loading', 'success', 'error']
-- Define allowed transitions
set :transitions to {
idle: ['loading'],
loading: ['success', 'error'],
success: ['idle'],
error: ['idle']
}
-- Transition with validation
on transition(newState)
if :transitions[:state].includes(newState)
set oldState to :state
set :state to newState
-- Execute exit action for old state
send exitState(oldState)
-- Execute entry action for new state
send enterState(newState)
-- Update UI
send updateUI
else
log 'Invalid transition from ' + :state + ' to ' + newState
end
end
Advanced Patterns:
<!-- Nested state machines -->
<div _="init
set :parentState to 'active'
set :childState to 'editing'
end
on transition(newState)
if newState is in :allowedStates
set :parentState to newState
-- Reset child state on parent transition
set :childState to getInitialChildState(newState)
end
end">
</div>
<!-- State machine with history -->
<div _="init
set :stateStack to ['idle']
end
on pushState(newState)
call :stateStack.push(:state)
set :state to newState
send updateUI
end
on popState
if :stateStack.length > 0
set :state to :stateStack.pop()
send updateUI
end
end">
</div>
<!-- Timed transitions -->
<div _="on enterState(state)
if state is 'processing'
wait 5s
if :state is still 'processing'
send transition('timeout')
end
end
end">
</div>
<!-- Parallel states -->
<div _="init
set :connectionState to 'disconnected'
set :authState to 'unauthenticated'
set :dataState to 'empty'
end
on connect
set :connectionState to 'connected'
if :authState is 'authenticated'
send loadData
end
end">
</div>