Skip to main content

Using the App Router

Foundation is mostly built on the Next.js Pages Router (src/pages), but it also supports the App Router (src/app). For general App Router setup with authentication (Server Actions, middleware, forms), see the blueprint-auth App Router guide. This page instead walks through Foundation's own App Router reference implementation.

Reference implementation: the demo onboarding wizard​

Foundation has a working App Router example at apps/foundation/src/app/demo-onboarding. It's a multi-step form wizard (quote → tariffs → details → review → success) built to prove out createNextWizard from @krakentech/blueprint-onboarding on the App Router, using Server Actions instead of an API route.

Layout​

app/demo-onboarding/layout.tsx wraps every step in the shared UIProvider and page chrome, the same role _app.tsx plays for the Pages Router.

Shared wizard config​

The wizard's steps, form types, and validation are split into three files under domains/onboarding/shared/utils, so isomorphic code never accidentally pulls in server-only APIs:

  • wizard-common.ts — form value types, enums, and (de)serialization for storing state in a cookie. Shared by both routers.
  • wizard-app-router.ts — the App Router's step list and yup validation schemas. Client-safe (no next/headers).
  • wizard-app-router-server.ts — the App Router's demoWizardAppRouter instance, built with createNextWizard from @krakentech/blueprint-onboarding/next-wizard/app-router. Server-only, because createNextWizard reads/writes cookies via next/headers.

A step page​

Each step folder has a page.tsx (server component) and a *Client.tsx (client component). quote/page.tsx shows the pattern:

export default async function QuotePage() {
const state = await demoWizardAppRouter.getCookieState();

async function submitStep(newState: DemoOnboardingState) {
"use server";
await demoWizardAppRouter.setCookieState({ state: newState });
}

return (
<QuoteClient
serializedState={demoWizardAppRouter.serializeState(state)}
submitStep={submitStep}
/>
);
}

The server component reads the wizard's cookie state and passes it down as serialized data, along with an inline Server Action for saving it back. QuoteClient.tsx is a "use client" component that renders the Formik form; on submit it calls submitStep (the Server Action) to persist state, then router.push to move to the next step client-side.

Later steps (details, review) use a standalone actions.ts file with a top-level "use server" Server Action instead of an inline one — either pattern works, use whichever reads more clearly for the page.

See also​