# Example: Simple 3-step workflow
from fasthtml.common import Div, H2, P, Form, Input, Label
# Define render functions for each step
def render_step1(ctx: InteractionContext):
"""Render first step - collect name."""
current_name = ctx.get("name", "")
return Div(
H2("Step 1: Enter Your Name"),
Label("Name:"),
Input(name="name", value=current_name, required=True)
)
def render_step2(ctx: InteractionContext):
"""Render second step - collect email."""
name = ctx.get("name", "User")
current_email = ctx.get("email", "")
return Div(
H2(f"Step 2: Hi {name}, enter your email"),
Label("Email:"),
Input(name="email", type="email", value=current_email, required=True)
)
def render_step3(ctx: InteractionContext):
"""Render third step - confirm."""
name = ctx.get("name", "")
email = ctx.get("email", "")
return Div(
H2("Step 3: Confirm"),
P(f"Name: {name}"),
P(f"Email: {email}")
)
# Define completion handler
def on_complete(state: Dict[str, Any], request):
"""Handle workflow completion."""
return Div(
H2("Success!"),
P(f"Welcome {state.get('name')}!"),
P(f"We'll email you at {state.get('email')}")
)
# Create the step flow - uses InMemoryWorkflowStateStore by default
registration_flow = StepFlow(
flow_id="registration",
steps=[
Step(
id="name",
title="Enter Name",
render=render_step1,
data_keys=["name"]
),
Step(
id="email",
title="Enter Email",
render=render_step2,
data_keys=["email"]
),
Step(
id="confirm",
title="Confirm",
render=render_step3,
next_button_text="Complete Registration"
)
],
on_complete=on_complete,
show_progress=True
)
# Generate router
registration_router = registration_flow.create_router(prefix="/register")
# In your FastHTML app, register the router:
# from cjm_fasthtml_app_core.core.routing import register_routes
# register_routes(app, registration_router)
#
# Or directly:
# registration_router.to_app(app)Step Flow
Step Definition
The Step class defines a single step in a multi-step workflow. Each step has: - Unique identifier - Title for display - Render function that generates the UI - Optional validation function - Optional data loader for fetching required data - State keys that this step manages
Step
def Step(
id:str, title:str, render:Callable, validate:Optional=None, data_loader:Optional=None, data_keys:List=<factory>,
can_skip:bool=False, show_back:bool=True, show_cancel:bool=True, next_button_text:str='Continue',
on_enter:Optional=None, on_leave:Optional=None
)->None:
Definition of a single step in a multi-step workflow.
StepFlow Class
The StepFlow class manages a multi-step workflow. It: - Tracks current step - Manages navigation between steps - Uses a WorkflowStateStore for server-side state persistence - Generates routes automatically - Provides resumability (returns to last valid step on page load)
StepFlow
def StepFlow(
flow_id:str, # Unique identifier for this workflow
steps:List, # List of step definitions
state_store:Optional=None, # Storage backend (defaults to InMemoryWorkflowStateStore)
container_id:str='step-flow-container', # HTML ID for content container
on_complete:Optional=None, # Completion handler
show_progress:bool=False, # Whether to show progress indicator
wrap_in_form:bool=True, # Whether to wrap content + navigation in a form
debug:bool=False, # Whether to print debug information
):
Manage multi-step workflows with automatic route generation and state management.
Step Management Methods
StepFlow.get_step
def get_step(
step_id:str, # Step identifier
)->Optional: # Step object or None
Get step by ID.
StepFlow.get_step_index
def get_step_index(
step_id:str, # Step identifier
)->Optional: # Step index or None
Get step index by ID.
State Management Methods
StepFlow.get_workflow_state
def get_workflow_state(
sess:Any, # FastHTML session object
)->Dict: # All workflow state
Get all workflow state from state store.
StepFlow.update_workflow_state
def update_workflow_state(
sess:Any, # FastHTML session object
updates:Dict, # State updates
)->None:
Update workflow state with new values.
StepFlow.clear_workflow
def clear_workflow(
sess:Any, # FastHTML session object
)->None:
Clear all workflow state.
Context and Rendering Methods
StepFlow.create_context
def create_context(
request:Any, # FastHTML request object
sess:Any, # FastHTML session object
step:Step, # Current step
)->InteractionContext: # Interaction context for rendering
Create interaction context for a step.
StepFlow.render_progress
def render_progress(
sess:Any, # FastHTML session object
)->FT: # Progress indicator or empty Div
Render progress indicator showing all steps.
StepFlow.render_step_content
def render_step_content(
step_obj:Step, # Step to render
ctx:InteractionContext, # Interaction context
next_route:str, # Route for next/submit
back_route:Optional=None, # Route for back
cancel_route:Optional=None, # Route for cancel
)->FT: # Complete step content with optional progress and navigation
Render step content with optional progress indicator and navigation.
Route Generation
StepFlow.create_router
def create_router(
prefix:str='', # URL prefix for routes (e.g., "/transcription")
)->APIRouter: # APIRouter with generated routes
Create FastHTML router with generated routes for this flow.
Usage Example
Here’s a complete example showing how to create a multi-step workflow. The StepFlow class uses InMemoryWorkflowStateStore by default, so you don’t need to provide a state store for simple use cases.
# Test step flow navigation
print(f"Flow has {len(registration_flow.steps)} steps")
print(f"First step: {registration_flow.steps[0].id}")
print(f"Last step: {registration_flow.steps[-1].id}")
print(f"Next after 'name': {registration_flow.get_next_step_id('name')}")
print(f"Previous before 'email': {registration_flow.get_previous_step_id('email')}")Flow has 3 steps
First step: name
Last step: confirm
Next after 'name': email
Previous before 'email': name