import { Wizard, Button, useWizardGrid } from "@cloudflare/kumo";
import { useCreateWorkerDemo } from "./use-create-worker-demo";
import { SelectMethodBody, SelectTemplateBody, SelectTemplateFooter, SetupBody, SetupFooter, DeployStepBody, DeployStepFooter, DeployingOverlay, UploadStepBody, UploadStepFooter } from "./wizard-demo-steps";

export function WizardFullDemo() {
  const { gridProps, onActiveStepElementChange } = useWizardGrid();

  const demo = useCreateWorkerDemo(); // Demo-only state plumbing, not part of the Wizard API.

  return (
    <>
      <Button onClick={demo.handleOpen}>Open Wizard</Button>
      <Wizard.Fullscreen open={demo.open} onClose={demo.handleClose}>
        <Wizard.Grid {...gridProps} title="Create a Worker">
          <Wizard
            step={demo.stepKey}
            onStepChange={demo.handleStepChange}
            onActiveStepElementChange={onActiveStepElementChange}
            lockTabMenu={demo.isDeploying}
          >
            <Wizard.Steps>
              <Wizard.Step stepKey="method" label="Select a method">
                <Wizard.Page
                  title="Ship something new"
                  description="Select from a Git repo, drag and drop your code or select a template."
                >
                  <SelectMethodBody onSelectMethod={demo.setSelectedMethod} />
                </Wizard.Page>
              </Wizard.Step>
              <Wizard.Step
                stepKey="template"
                label="Select a template"
                when={demo.isTemplatePath}
              >
                <Wizard.Page
                  title="Select a template"
                  description="Get up and running with one of our full-stack application examples."
                  footer={<SelectTemplateFooter />}
                >
                  <SelectTemplateBody
                    onSelectTemplate={demo.setSelectedTemplate}
                  />
                </Wizard.Page>
              </Wizard.Step>
              <Wizard.Step
                stepKey="setup"
                label="Deploy"
                when={demo.isTemplatePath}
              >
                <div className="relative">
                  <Wizard.Page
                    title="Set up your application"
                    description="Configure your Worker project and deploy it to Cloudflare."
                    footer={
                      <SetupFooter
                        isDeploying={demo.isDeploying}
                        onDeploy={demo.handleTemplateDeploy}
                      />
                    }
                  >
                    <SetupBody
                      workerName={demo.workerName}
                      onWorkerNameChange={demo.setWorkerName}
                      nameStatus={demo.nameStatus}
                      nameError={demo.nameError}
                    />
                  </Wizard.Page>
                  {demo.isDeploying && (
                    <DeployingOverlay projectName={demo.deployProjectName} />
                  )}
                </div>
              </Wizard.Step>
              <Wizard.Step
                stepKey="deploy"
                label="Deploy Worker"
                when={!demo.isTemplatePath && !demo.isUploadPath}
              >
                <div className="relative">
                  <Wizard.Page
                    title="Deploy Hello World"
                    description="A simple Worker that returns 'Hello World!'. Perfect for getting started."
                    footer={
                      <DeployStepFooter
                        isDeploying={demo.isDeploying}
                        onDeploy={demo.handleDeploy}
                      />
                    }
                  >
                    <DeployStepBody
                      workerName={demo.workerName}
                      onWorkerNameChange={demo.setWorkerName}
                      nameStatus={demo.nameStatus}
                      nameError={demo.nameError}
                    />
                  </Wizard.Page>
                  {demo.isDeploying && (
                    <DeployingOverlay projectName={demo.deployProjectName} />
                  )}
                </div>
              </Wizard.Step>
              <Wizard.Step
                stepKey="upload"
                label="Upload and deploy"
                when={demo.isUploadPath}
              >
                <div className="relative">
                  <Wizard.Page
                    title="Upload and deploy"
                    description="Drag and drop your static files and configure deployment settings."
                    footer={
                      <UploadStepFooter
                        isDeploying={demo.isDeploying}
                        onDeploy={demo.handleTemplateDeploy}
                      />
                    }
                  >
                    <UploadStepBody />
                  </Wizard.Page>
                  {demo.isDeploying && (
                    <DeployingOverlay projectName={demo.deployProjectName} />
                  )}
                </div>
              </Wizard.Step>
            </Wizard.Steps>
          </Wizard>
        </Wizard.Grid>
      </Wizard.Fullscreen>
    </>
  );
}

Installation

Barrel

import { Wizard, useWizard, useWizardGrid } from "@cloudflare/kumo";

Granular

import { Wizard, useWizard, useWizardGrid } from "@cloudflare/kumo/components/wizard";

Anatomy

The Wizard is a compound component. Compose only the pieces you need:

Wizard.Fullscreen Portal overlay
Wizard.Grid Optional Decorative wireframe
Wizard Step state, context, and navigation
Wizard.Steps Filters and indexes active steps
Wizard.Step Individual step with card-stack animation
Wizard.Page Card surface with title, description, body, and footer

Usage

import { Button, Wizard, useWizard, useWizardGrid } from "@cloudflare/kumo";

function CreateWorker({ open, onClose }) {
  const { gridProps, onActiveStepElementChange } = useWizardGrid();

  return (
    <Wizard.Fullscreen open={open} onClose={onClose}>
      <Wizard.Grid title="Create a Worker" {...gridProps}>
        <Wizard onActiveStepElementChange={onActiveStepElementChange}>
          <Wizard.Steps>
            <Wizard.Step stepKey="method" label="Select a method">
              <Wizard.Page
                title="Ship something new"
                description="Choose how you want to start."
                footer={<Footer />}
              >
                {/* ... */}
              </Wizard.Page>
            </Wizard.Step>
            {/* ...more steps */}
          </Wizard.Steps>
        </Wizard>
      </Wizard.Grid>
    </Wizard.Fullscreen>
  );
}

function Footer() {
  const { back, next, isFirstStep } = useWizard();

  return (
    <>
      <Button variant="ghost" onClick={back} disabled={isFirstStep}>
        Back
      </Button>
      <Button onClick={next}>Continue</Button>
    </>
  );
}

Hooks

useWizard()

Access wizard state from any descendant component. Must be used within a Wizard.

const { step, stepKey, goToStep, next, back, isLastStep, isFirstStep } = useWizard();
PropertyTypeDescription
back() => void

Go back to the previous step. No-op on the first step.

complete() => void

Fires the onComplete callback. Not gated by isLastStep — the consumer decides when to call it.

goToStep(key: string) => void

Navigate to a step by its stepKey. No-op if the key is not mounted.

isChangingStepboolean

Whether an onBeforeStepChange guard is pending. Use to show a loading state on navigation buttons.

isFirstStepboolean

Whether the wizard is on its first step.

isLastStepboolean

Whether the wizard is on its last registered step.

itemsWizardStepItem[]

Array of registered step metadata (key, label, hideFromSidebar).

lockTabMenuboolean

Whether backward navigation is locked.

next() => void

Advance to the next mounted step. No-op on the last step.

onStepChange(step: number) => void

Navigate to a step by numeric index. Prefer goToStep, next, or back.

stepnumber

Current active step index (0-based).

stepKeystring | undefined

Key of the current active step (Wizard.Step’s stepKey).

totalStepsnumber

Total number of registered steps.

useWizardGrid()

Bridges Wizard and Wizard.Grid by tracking the active card’s height and managing transition state. Returns gridProps to spread onto Wizard.Grid.

import { useWizardGrid } from "@cloudflare/kumo";

const { gridProps, onActiveStepElementChange } = useWizardGrid();

<Wizard.Grid {...gridProps}>
  <Wizard step={step} onStepChange={setStep}>
    <Wizard.Steps>...</Wizard.Steps>
  </Wizard>
</Wizard.Grid>;
ReturnTypeDescription
gridProps{ activeCardHeight: number; isTransitioning: boolean }Spread onto Wizard.Grid.
onActiveStepElementChange(el: HTMLDivElement | null) => void

Callback for step element tracking.

API Reference

Wizard

Root context provider and animation engine. Must be rendered inside a Wizard.Fullscreen.

PropTypeDefaultDescription
childrenReactNode-Wizard content — `Wizard.Steps`, `Wizard.Step`, etc.
classNamestring-Additional CSS classes merged via `cn()`.
defaultStepnumber | string0Initial step (index or `stepKey`) for uncontrolled mode. Ignored when `step` is provided.
labelsWizardLabels-Labels for internationalization of aria-labels. All labels have English defaults.
lockTabMenubooleanfalseWhen true, prevents clicking previous steps to navigate back.
sidebarbooleantrueRenders the step-indicator sidebar. Visible at wide viewports (`@5xl`).
stepnumber | string-Current active step (0-based index or stepKey string). Controlled mode.
onStepChange(index: number, key: string) => void-Callback when step changes. Receives both the numeric index and the step key.
onBeforeStepChange(from: number, to: number) => boolean | Promise<boolean>-Async validation guard fired before every step transition. Return false to block.
onComplete() => void-Fired when the user advances past the last step (e.g. clicks Done).
onActiveStepElementChange(element: HTMLDivElement | null) => void-Callback invoked when the active step DOM element changes. Used by useWizardGrid().

Wizard.Fullscreen

Fullscreen modal container with a floating close button, Escape-to-close, scroll lock, and role="dialog". Content fills the entire viewport.

PropTypeDefaultDescription
classNamestring-Override content wrapper classes.
openboolean-Controls visibility. Returns null when false.
onClose() => void-Called on Escape or close button click.
containerPortalContainerdocument.bodyPortal target element. Overrides KumoPortalProvider context.
showCloseButtonbooleantrueWhether to show the floating close button.
labelsWizardFullscreenLabels-Labels for i18n of aria-labels. close: aria-label for the close button (default: Close).

Wizard.Grid

Decorative animated wireframe with dashed borders, corner crosses, and an optional left-side title. The bottom border and crosses animate with the active card’s measured height.

PropTypeDefaultDescription
classNamestring-Additional CSS classes.
activeCardHeight*number-Measured card height for border animation. Use useWizardGrid().
isTransitioning*boolean-Whether a step transition is in progress. Use useWizardGrid().
titlestring-Left-side title, visible at @5xl/wizard (64rem).
topOffsetnumber147Distance from viewport top to the grid.
width"narrow" | "wide""narrow"Max-width for the wireframe decoration and card content. narrow = 38rem, wide = 48rem.

Wizard.Steps

Container for wizard steps. Injects the index prop into each child Wizard.Step.

PropTypeDefaultDescription
classNamestring-Additional CSS classes for the step container.

Wizard.Step

Individual step definition. Registers metadata with the Wizard context and wraps children in a card-stack animation.

PropTypeDefaultDescription
stepKey*string-Unique identifier for this step.
labelstring-Label shown in the sidebar navigation.
hideFromSidebarbooleanfalseHides this step from the sidebar list.
whenbooleantrueWhether this step is active in the current flow. When false, the step is excluded from rendering, indexing, sidebar, and navigation.

Wizard.Page

Card layout inside a wizard step. Wraps content in a LayerCard with optional title, description, scrollable body, and footer.

PropTypeDefaultDescription
classNamestring-Additional CSS classes for the card.
titlestring-Card heading text.
descriptionstring | ReactNode-Description shown below the title.
footerReactNode-Footer content (e.g., navigation buttons). Rendered outside the primary card surface.
maxHeightstring"~100vh − 400px"Max height of the card. Override with any CSS value (e.g. "600px", "80dvh").