Security News
The Dark Side of Open Source
At Node Congress, Socket CEO Feross Aboukhadijeh uncovers the darker aspects of open source, where applications that rely heavily on third-party dependencies can be exploited in supply chain attacks.
react-wizard-primitive
Advanced tools
Readme
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.
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.
react-wizard-primitive
npm i react-wizard-primitive
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.
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.
Wizard
Component as your wizard rootimport 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.
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.
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.
number
The index of the current step
boolean
Is the state the currently active one?
boolean
Has the step been active before?
function
Move to the step after this step.
function
Move to the step before this step.
function
Set this step to be currently active. Set hasBeenActive for all following steps to false.
function
Set this step to be currently active. All following steps will keep the activated state.
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
object
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.
number
Currently active step
number
Index of the furthest step, that has been activated
number
Index of the furthest step, that has been activated
function
Call this to proceed to the next step
function
Call this to proceed to the previous step
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.
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.
// 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
WizardStepOptions
(optional)
It can take an optional options object, which can take a
routeTitle
See routing for more details.
Returns
// 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.
// 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.
// isActive will be true if this wizardStep should be rendered, nextStep will move to the next step
<WizardStep>
{
({isActive, nextStep}) => {
...
}
}
</WizardStep>
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.
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.
<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>
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.
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.
Same example, but implemented with the render props API.
This example demonstrates, how you can build a wizard that displays the steps one after another, but keeps the already displayed steps around.
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>
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)
Johannes Kling 💻 📖 🤔 💡 ⚠️ | Jose Miguel Bejarano 🤔 | kaYcee 🤔 | Kevin Aldebert 🤔 | Carlos Santos 🐛 | Andrei Benea 🐛 |
FAQs
A react wizard primitive without UI restrictions - hooks and render props API available!
The npm package react-wizard-primitive receives a total of 1,458 weekly downloads. As such, react-wizard-primitive popularity was classified as popular.
We found that react-wizard-primitive demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
At Node Congress, Socket CEO Feross Aboukhadijeh uncovers the darker aspects of open source, where applications that rely heavily on third-party dependencies can be exploited in supply chain attacks.
Research
Security News
The Socket Research team found this npm package includes code for collecting sensitive developer information, including your operating system username, Git username, and Git email.
Security News
OpenJS is warning of social engineering takeovers targeting open source projects after receiving a credible attempt on the foundation.