Skip to content

Form State

Multi-step forms can get complicated fast. Conditional visibility, validation logic, and routing must all be handled correctly. If poorly planned, large forms can quickly devolve into a tangled mess of spaghetti code.

To manage this complexity, Namesake implements a finite-state machine to ensure that a form can be in exactly one of a finite number of states at any given time. Changes from one state to another are handled through pre-determined transitions.

Each form’s state is managed using XState. The user begins in the title state, transitioning to various other states until they reach complete.

---
title: "Namesake Forms: State Machine"
---
stateDiagram-v2
    [*] --> Title

    Title --> Filling : START

    state Filling {
        currentStepId
    }

    Filling --> Title   : GOTO_TITLE
    Filling --> Filling : GOTO_STEP
    Filling --> Review  : GOTO_REVIEW

    Review --> Editing    : EDIT_STEP
    Review --> Filling    : GOTO_STEP
    Review --> Submitting : SUBMIT

    state Editing {
        editingStepId
    }

    Editing --> Review : SAVE_EDIT

    Submitting --> Complete : SUBMIT_DONE
    Submitting --> Review   : SUBMIT_ERROR

    Complete --> [*]
State What the user sees
title Cover page with form info and a “start” button
filling Individual questions
review Summary of all answers
editing A single step reopened from the review page
submitting Loading state while PDFs are generated
complete Success page with options to redownload or restart

The state machine also stores context:

Context Description
formSlug Set once when the machine is created
currentStepId Set on START and updated on each GOTO_STEP while in filling
editingStepId Set on EDIT_STEP when entering editing, then cleared on SAVE_EDIT

As the user progresses through the form, they will enter data which affects the visibility of other fields and steps. To make sure we are displaying the correct fields on the review table and writing the correct data to the final PDF, visibility must be resolved.

The resolveFormVisibility function takes an input of steps, user formData, and pdfs, and parses the visibility rules. It returns:

  • visibleStepIds — steps not excluded by when predicates
  • visibleFields — field values for visible fields only
  • sections — per-step arrays of visible field names (used to section the review table into scannable chunks)
  • pdfsToInclude — which PDFs to include based on their when predicates

Forms can be lengthy, so it’s important to save a user’s progress as they go. Field values and form progress are automatically saved to IndexedDB in the formProgress table. A form that is closed and reopened will resume where it left off.