Documentation menu

FormWorkflow

A form orchestration layer for the failure paths teams repeatedly rebuild: validation, server conflicts, focused summaries, duplicate-submit protection, draft recovery, unsaved changes, and step navigation.

Loading preview…

Installation

The CLI copies editable source and installs React Hook Form, its Zod resolver, and Zod. Elsecase is a shadcn registry, not a runtime package.

pnpm dlx shadcn@latest add https://elsecase.vercel.app/r/form-workflow.json

Basic usage

import { z } from "zod"
import { useFormContext } from "react-hook-form"
import { FormWorkflow } from "@/components/form-workflow"

const schema = z.object({
  name: z.string().min(2),
  website: z.string().url(),
})

<FormWorkflow
  schema={schema}
  defaultValues={{ name: "", website: "" }}
  onSubmit={saveOrganization}
  warnOnUnsavedChanges
>
  <OrganizationFields />
</FormWorkflow>

API reference

PropPurposeDefault
schemaA Zod object schema used before submit or autosave.Required
defaultValuesInitial form values and reset target.Required
onSubmitAsync function returning a FormSubmissionResult.Required
childrenFields registered through useFormContext.Required
modeSingle-page or multi-step rendering."single"
autosaveDebounced onSave callback, delay, and enabled flag.None
warnOnUnsavedChangesWarn for page unload and clicked links while dirty.false
successBehaviorMessage, reset, or preserve behavior after success."message"

Submission result

type FormSubmissionResult = {
  success: boolean
  message?: string
  fieldErrors?: Record<string, string>
  formError?: string
}

Field errors are mapped back into React Hook Form. A form-level message and every field error appear in a focusable summary while the entered values stay intact.

Multi-step forms

Set mode="multi-step" and wrap direct children inFormWorkflowStep. Each step declares the field paths it must validate before continuing; the final step owns submission.

Autosave and recovery

Autosave runs only after the current values pass the schema. The built-in status announces saving, success, and failure, and a failed draft exposes an explicit retry action.

Unsaved changes

The warning covers browser unloads and ordinary same-window anchor clicks. Router-specific programmatic navigation should be blocked by your routing layer because browsers do not expose one universal interception API.

Accessibility behavior

Invalid submissions move focus to a summary, summary actions focus the corresponding field, async statuses use live regions, steps use fieldsets and legends, and built-in actions meet a 44px minimum target.

Responsive behavior

Workflow actions stack at narrow widths, long summaries wrap, and the component imposes no fixed width. The application retains ownership of field layout and can collapse grids around the workflow.

Testing example

fireEvent.click(screen.getByRole("button", { name: "Save changes" }))
const summary = await screen.findByRole("alert")
await waitFor(() => expect(summary).toHaveFocus())
expect(onSubmit).not.toHaveBeenCalled()

Customization

FormWorkflow owns orchestration and its action/status surfaces. Your field components own labels, descriptions, inputs, and inline errors throughuseFormContext, so the copied source fits an existing design system.

Dependencies

React, react-hook-form@^7.85.0,@hookform/resolvers@^5.8.0, and zod@^4.4.3.

Known limitations

Version 0.1 supports Zod object schemas because registered form fields must have named paths. File upload transport, cross-tab draft merging, programmatic router guards, and remote step persistence remain application concerns.

Source

Review the implementation and tests on GitHub:
github.com/heysinghaaa/Elsecase/tree/main/registry/form-workflow