npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-wizard-primitive

v2.5.0

Published

A react wizard primitive without UI restrictions - hooks and render props API available!

Downloads

6,781

Readme

Build Coverage License Version Types Size Dependencies Pull Requests welcome Downloads semantic-release

github stars

The Problem

You need to implement a wizard / stepper, but have specific UI requirements. You want a flexible solution that suits a wide range of use cases. Check out the examples to see what's possible.

The Solution

React Wizard Primitive handles the state management and you bring the UI. Leverage a render props or hooks API to get rid of the tedious boilerplate. You can use this library to build other abstractions, that better suit your specific needs on top of it.

Quick Start

  1. Install react-wizard-primitive
npm i react-wizard-primitive
  1. Create your first wizard
import React from "react";
import { useWizard } from "react-wizard-primitive";

export default function App() {
  const { getStep, nextStep } = useWizard();
  const stepTitles = ["First", "Second", "Third"]; //let's render them one ofter the other

  return (
    <div>
      {stepTitles.map(
        (stepTitle) =>
          getStep().isActive && <div onClick={nextStep}>{stepTitle}</div>
      )}
    </div>
  );
}

See a working example here.

import React from "react";
import { Wizard } from "react-wizard-primitive";

export default function App() {
  const stepTitles = ["First", "Second", "Third"]; //let's render them one ofter the other

  return (
    <Wizard>
      {({ getStep, nextStep }) => (
        <div>
          {stepTitles.map(
            (stepTitle) =>
              getStep().isActive && <div onClick={nextStep}>{stepTitle}</div>
          )}
        </div>
      )}
    </Wizard>
  );
}

See a working example here.

Splitting the wizard in multiple components

Using the getStep function is great if you are creating a small wizard which can live inside a single component.

If your wizard grows it can come in handy to separate each step inside it's own component. react-wizard-primitive makes this really easy.

  1. Put a Wizard Component as your wizard root
  2. Create Step components. All step components inside one Wizard Component work together.
import React from "react";
import { Wizard, useWizardStep } from "react-wizard-primitive";

const FirstStep = () => {
  const { isActive, nextStep } = useWizardStep();
  return isActive ? <div onClick={nextStep}>First Step</div> : null;
};

const SecondStep = () => {
  const { isActive, nextStep } = useWizardStep();
  return isActive ? <div onClick={nextStep}>Second Step</div> : null;
};

export default function App() {
  return (
    <Wizard>
      <FirstStep />
      {/* a step doesn't need to be a direct child of the wizard. It can be nested inside of html or react components, too!*/}
      <div>
        <SecondStep />
      </div>
    </Wizard>
  );
}

See a working example here.

import React from "react";
import { Wizard, WizardStep } from "react-wizard-primitive";

const FirstStep = () => {
  return (
    <WizardStep>
      {({ isActive, nextStep }) =>
        isActive ? <div onClick={nextStep}>First Step</div> : null
      }
    </WizardStep>
  );
};

const SecondStep = () => {
  return (
    <WizardStep>
      {({ isActive, nextStep }) =>
        isActive ? <div onClick={nextStep}>Second Step</div> : null
      }
    </WizardStep>
  );
};

export default function App() {
  return (
    <Wizard>
      <FirstStep />
      {/* a step doesn't need to be a direct child of the wizard. It can be nested inside of html or react components, too!*/}
      <div>
        <SecondStep />
      </div>
      {/* WizardStep can also be used without placing it inside another component*/}
      <WizardStep>
        {({ isActive }) => (isActive ? <div>Third Step</div> : null)}
      </WizardStep>
    </Wizard>
  );
}

See a working example here.

Building your own abstractions

Sometimes you need a wizard in multiple places, but keep the styling consistent. react-wizard-primitive provides you with basic building blocks that you can use to build powerful abstractions on top of it.

<MyCustomWizard>
  <MyCustomWizard.Step>
    <TextFields />
  </MyCustomWizard.Step>
  <MyCustomWizard.Step>
    <div>Just some other inline jsx</div>
  </MyCustomWizard.Step>
  <MyCustomWizard.Step>
    <div>And another one</div>
  </MyCustomWizard.Step>
  <MyCustomWizard.Step>
    <div>Last one</div>
  </MyCustomWizard.Step>
</MyCustomWizard>

See a working example here.

API

Step

A step is the main data structure for the wizard. It is returned by the getStep call and provided by useWizardStep and the WizardStep component.

  • index number

    The index of the current step

  • isActive boolean

    Is the state the currently active one?

  • hasBeenActive boolean

    Has the step been active before?

  • nextStep function

    Move to the step after this step.

  • previousStep function

    Move to the step before this step.

  • resetToStep function

    Set this step to be currently active. Set hasBeenActive for all following steps to false.

  • moveToStep function

    Set this step to be currently active. All following steps will keep the activated state.

  • goToStep function(index:number)

    Go to the step with the given index

useWizard

A hook that manages the state of the wizard and provides you with functions to interact with it

Arguments

  • options object (optional)

    • initialStepIndex number (optional)

      The provided step index will be displayed initially. All previous steps will be treated as if they've been already activated.

    • onChange function({newStepIndex : number, previousStepIndex: number, maxActivatedStepIndex : number}) (optional)

      Is called every time the wizard step changes.

Returns

  • wizard object
    • getStep function(options?) : Step

      Creates a wizard step and provides it's current state. It can take an optional options object, which can take a routeTitle See routing for more details.

    • activeStepIndex number

      Currently active step

    • maxActivatedStepIndex number

      Index of the furthest step, that has been activated

    • maxActivatedStepIndex number

      Index of the furthest step, that has been activated

    • nextStep function

      Call this to proceed to the next step

    • previousStep function

      Call this to proceed to the previous step

    • moveToStep function(stepIndex : number, options? : {skipOnChangeHandler?: boolean})

      Move to step with index stepIndex. You can pass in options to control if the onChange handler should be called for this operation.

    • resetToStep function(stepIndex : number, options? : {skipOnChangeHandler?: boolean})

      Move to step with index stepIndex. Set hasBeenActive for all following steps as well as the new step to false. You can pass in options to control if the onChange handler should be called for this operation.

Example

// start at third step and log every change
const { getStep } = useWizard({
  initialStepIndex: 2,
  onChange: ({ newStepIndex, previousStepIndex }) => {
    console.log(`I moved from step ${previousStepIndex} to ${newStepIndex}`);
  },
});

useWizardStep

A hook that let's you split your wizard into separate components and creates a wizard step. It calls getStep under the hood.

Arguments

  • options WizardStepOptions (optional)

    It can take an optional options object, which can take a routeTitle See routing for more details.

Returns

Example

// isActive will be true if this wizardStep should be rendered, nextStep will move to the next step
const { isActive, nextStep } = useWizardStep();

Wizard

A component that servers as the root for a wizard if you choose to split your wizard into multiple components.

Otherwise it can be used as a replacement for the useWizard hook. It takes the same arguments (as props) and returns the same values to the render prop.

Example

// start at third step and log every change
<Wizard initialStepIndex="2" onChange={({newStepIndex, previousStepIndex}) => {
  console.log(`I moved from step ${previousStepIndex} to ${newStepIndex}`);
}}>
{
  ({getStep}) => {
    ...
  }
}
</Wizard>

WizardStep

A component that serves as an alternative to the useWizardStep hook. It takes the same arguments (as props) and returns the same values to the render prop.

Example

// isActive will be true if this wizardStep should be rendered, nextStep will move to the next step
<WizardStep>
{
  ({isActive, nextStep}) => {
    ...
  }
}
</WizardStep>

Routing

Basics

Out of the box react-wizard-primitive supports an opt-in routing via hash.

In order to use it, you need to specify a routeTitle in the getStep call or pass it as a prop to the WizardStep or useWizardStep hook. The routeTitle will be used as the hash.

If no routeTitle is provided, react-wizard-primitive won't make any changes to the URL. If only some steps are provided with a title, we assume that this happened by mistake, and won't change the url either. Instead we log a warning to the console, indicating which steps are missing a title.

Initial Hash Route

If a hash is present when the wizard is first rendered, it will try to find a matching step to that hash and jump to it or otherwise jump to the initial step.

You can use this behaviour to start the wizard at any given point.

Example

<Wizard>
  {"yourdomain.com/#/first-step"}
  <WizardStep routeTitle="first-step">
    {({ isActive, nextStep }) =>
      isActive && <div onClick={nextStep}>Step 1</div>
    }
  </WizardStep>

  {"yourdomain.com/#/second-step"}
  <WizardStep routeTitle="second-step">
    {({ isActive, nextStep }) =>
      isActive && <div onClick={nextStep}>Step 2</div>
    }
  </WizardStep>

  {"yourdomain.com/#/third-step"}
  <WizardStep routeTitle="third-step">
    {({ isActive, nextStep }) =>
      isActive && <div onClick={nextStep}>Step 3</div>
    }
  </WizardStep>
</Wizard>

Examples

You can build nearly anything on top of react-wizard-primitive. Take a look at those examples to get an idea of what's possible.

🔗 Basic Hooks

This is a good starting point, if you want to see a basic hook implementation. A classical wizard, which displays the steps one after the other.

Basic Example

🔗 Basic Render Props

Same example, but implemented with the render props API.

🔗 Buildup Wizard

This example demonstrates, how you can build a wizard that displays the steps one after another, but keeps the already displayed steps around.

Buildup Wizard

🔗 Custom Abstraction

It can get tedious to work with the basic building blocks and repeat styling or display handling all over again. This example demonstrates how you can build your own abstractions on top of react-wizard-primitive.

<MyCustomWizard>
  <MyCustomWizard.Step>
    <TextFields />
  </MyCustomWizard.Step>
  <MyCustomWizard.Step>
    <div>Just some other inline jsx</div>
  </MyCustomWizard.Step>
  <MyCustomWizard.Step>
    <div>And another one</div>
  </MyCustomWizard.Step>
  <MyCustomWizard.Step>
    <div>Last one</div>
  </MyCustomWizard.Step>
</MyCustomWizard>

Buildup Wizard

Migration from older versions

Upgrading from v1

  • hasBeenActive is now false on first render. To achieve the previous behavior you can modify your code to hasBeenActive || isActive
  • maxVisitedStepIndex has been renamed to maxActivatedStepIndex and will not include the currently active step if it's first rendered. To achieve the previous behavior you can modify your code to Math.max(maxActivatedStepIndex, activeStepIndex)

Contributors