App Router - API Reference
📘 NOTE: For App Router applications, import from
'@krakentech/blueprint-onboarding/next-wizard/app-router'.
The App Router Wizard is a set of utils which handle server side state persistence, submission validation and routing for journeys which span across multiple pages. Using the builder pattern, extensive type safety is provided. This version is specifically designed for Next.js App Router (app/ directory) usage with Server Components and Server Actions.
The createNextWizard() builder function​
There are multiple configuration options in the createNextWizard builder function, which are explained in the following.
import { createNextWizard } from '@krakentech/blueprint-onboarding/next-wizard/app-router';
export const wizard = createNextWizard({
cookieName,
encryptionSecret,
initialState,
steps,
serializeState,
deserializeState,
cookieOptions,
});
cookieName​
The name of the cookie that stores the state. Ensure that this name is unique to avoid conflicts with other cookies.
encryptionSecret (optional)​
The secret used to encrypt the cookie state. If no secret is provided then the state will not be encrypted.
NOTE: If the secret is changed then the getCookieState function will delete the cookie and return default values for the cookie data.
initialState​
The initial state which is also used as a fallback value if there is an issue with the retrieval of existing state.
steps​
The steps config is used to control the routing between the steps. Each step requires a name as identifier and pathname for navigation. The configuration can be defined as a static array, or as a dynamic function which is dependant on the state.
// Static Steps Config
const staticStepsConfig = [
{
name: StepName.Supply,
pathname: '/onboarding/1-supply',
},
{
name: StepName.TariffSelection,
pathname: '/onboarding/2-tariff-selection',
},
...
] as const satisfies WizardSteps<StepName>;
This example shows a config where the steps are based on a tariff choice.
const getStepsConfig: WizardStepsConfig<TStepName, TState> = (state) => {
if (state.formValues.tariffName === 'octo_basic') {
return stepsConfigA;
}
return stepsConfigB;
};
query​
Steps can optionally include a query field that can either be static using a constant definition, or dynamic using a function definition.
// static query params
const steps = [
{
name: StepName.EditPersonalDetails,
pathname: '/onboarding/3-personal-details',
query: {
edit: 'true',
},
...
}
] as const satisfies WizardSteps<StepName>;
// dynamic query params
const getSteps = (state: TState) => [
{
name: StepName.Supply,
pathname: '/onboarding/1-supply',
query: {
affiliate: state.formValues.affiliateCode,
},
...
}
] as const satisfies WizardSteps<StepName>;
serializeState / deserializeState​
The wizard requires a serializeState function which translates the state object into a format that can be stored (cookie) or transmitted and reconstructed with the deserializeState function later.
The following example shows how to (de-)serialize date values.
import { parseISO } from 'date-fns';
type FormValues = {
// ...
birthday: Date | null;
};
type SerializedFormValues = Omit<FormValues, 'birthday'> & {
birthday: string | null;
};
type WizardState = {
formValues: FormValues;
};
type WizardSerializedState = {
formValues: SerializedFormValues;
};
const serializeState = (state: WizardState): WizardSerializedState => {
return {
...state,
formValues: {
...state.formValues,
birthday: state.formValues.birthday?.toISOString() ?? null,
},
};
};
const deserializeState = (
serializedState: WizardSerializedState
): WizardState => {
return {
...serializedState,
formValues: {
...serializedState.formValues,
birthday: serializedState.formValues.birthday
? parseISO(serializedState.formValues.birthday)
: null,
},
};
};
If the state doesn't require serialization, simply return the values back directly:
type State = { ... };
type SerializedState = State;
const serializeState = (state: State): SerializedState => state;
const deserializeState = (serializedState: SerializedState): State => serializedState;
Utils​
These utils are returned by the createNextWizardAppRouter builder function.
getInitialState​
Gets a structured clone of the initial state of the wizard.
getInitialState: () => TState;
getCookieState​
Gets the cookie state and deserializes it.
Uses next/headers cookies API internally.
It is meant to be used in Server Components or Route Handlers.
getCookieState: () => Promise<TState>;
setCookieState​
Sets the cookie state from a Server Action or Route Handler.
Uses next/headers cookies API internally.
setCookieState: (options: { state: TState }) => Promise<void>;
deleteCookieState​
Deletes the cookie state from a Server Action or Route Handler.
Uses next/headers cookies API internally.
IMPORTANT! When deleting a cookie and you're not relying on the default attributes, you must pass the exact same path and domain attributes that were used to set the cookie.
deleteCookieState: (options?: {
cookieOptions?: Pick<WizardCookieOptions, 'path' | 'domain'>;
}) => Promise<void>;
getNextStep​
Gets the next step based on the current step name and state.
getNextStep: (options: {
currentStepName: StepName<TStepNames>;
state: TState;
}) => WizardStep<StepName<TStepNames>>;
getPreviousStep​
Gets the previous step based on the current step name and state.
getPreviousStep: (options: {
currentStepName: StepName<TStepNames>;
state: TState;
}) => WizardStep<StepName<TStepNames>>;
getStep​
Gets a specific step by its name and state.
getStep: (options: { stepName: StepName<TStepNames>; state: TState }) =>
WizardStep<StepName<TStepNames>>;
getSteps​
Gets the currently active steps from the steps config.
If the steps are dynamic (a function), it evaluates them based on the current state.
getSteps: (state: TState) => ReadonlyArray<WizardStep<StepName<TStepNames>>>;
serializeState​
Serializes the state into a storable format (e.g., for cookies).
serializeState: (state: TState) => TSerializedState;
deserializeState​
Deserializes the state after retrieving it from the cookie.
deserializeState: (serializedState: TSerializedState) => TState;
getStepDestination​
Gets the destination URL for a given step.
Use this with useRouter().push() from next/navigation for navigation.
getStepDestination: (step: WizardStep<StepName<TStepNames>>) => string;
Usage example:
'use client';
import { useRouter } from 'next/navigation';
function NavigationButton({ state, currentStep }) {
const router = useRouter();
const handleNext = () => {
const nextStep = wizard.getNextStep({
currentStepName: currentStep,
state,
});
router.push(wizard.getStepDestination(nextStep));
};
return <button onClick={handleNext}>Next</button>;
}
getRedirectStep​
Gets the redirect step for verification or validation problems.
This is typically the first step that hasn't been submitted yet.
getRedirectStep: (options: {
state: TState;
submittedSteps: StepName<TStepNames>[];
}) => WizardStep<StepName<TStepNames>>;
getRedirectDestination​
Gets the redirect destination URL for verification or validation problems.
getRedirectDestination: (options: {
state: TState;
submittedSteps: StepName<TStepNames>[];
}) => string;
redirectToStep​
Redirects to the appropriate step using Next.js App Router redirect.
This function does not return - it throws a redirect.
Use this in Server Components when a redirect is needed.
redirectToStep: (options: {
state: TState;
submittedSteps: StepName<TStepNames>[];
}) => never;
Usage example:
// In a Server Component
export default async function TariffsPage() {
const state = await wizard.getCookieState();
const isUnlocked = wizard.getIsStepUnlocked({
stepName: OnboardingStep.Tariffs,
state,
submittedSteps: state.submittedSteps,
});
if (!isUnlocked) {
// This will redirect and not return
wizard.redirectToStep({
state,
submittedSteps: state.submittedSteps,
});
}
return <TariffsForm state={state} />;
}
getIsStepUnlocked​
Verifies if the given step is already unlocked.
A step is unlocked if all previous steps have been submitted.
getIsStepUnlocked: (options: {
stepName: StepName<TStepNames>;
submittedSteps: StepName<TStepNames>[];
state: TState;
}) => boolean;
addToSubmittedSteps​
Adds a step to the list of submitted steps.
Typically used in the form submission handler to track progress.
addToSubmittedSteps: (options: {
stepName: StepName<TStepNames>;
submittedSteps: StepName<TStepNames>[];
}) => StepName<TStepNames>[];
Notable Differences from Pages Router​
The App Router version does not include the following methods that are present in the Pages Router version:
toNextStep- UsegetNextStep()+getStepDestination()withuseRouter().push()toPreviousStep- UsegetPreviousStep()+getStepDestination()withuseRouter().push()toStep- UsegetStep()+getStepDestination()withuseRouter().push()getRedirect- UseredirectToStep()insteadhandleQueryParams- Handle query params in your Server Component usingsearchParamssetCookieStateOnClient- Use Server Actions insteadsetCookieStateOnServer- UsesetCookieState()in Server ActionsgetCookieStateOnServer- UsegetCookieState()in Server ComponentsdeleteCookieOnServer- UsedeleteCookieState()in Server Actions
The App Router version uses next/headers for cookie operations, which works seamlessly with Server Components and Server Actions without requiring a separate API route.