6. Checkout page

Route between product, address, shipping, and payment steps.

On this page

The express checkout flow will have four steps:

  • Product variant selection: The customer will first open the page to view the product's details, choose its options, then add it to the cart.
  • Address details: The customer will input their shipping address, which will also be used as a billing address.
  • Shipping method selection: The customer will choose a shipping method used to deliver their order.
  • Payment and cart completion: The customer will choose a payment provider to make the payment, then places the order.

These steps will be available within a single page whose path is the product's handle (for example, sweatpants), but the routing between them will depend on the cart's current state and a step query parameter. In this section, you'll create this page and the router component used to set the current active step.

Tip: Each product in Aiminaabee has a handle property, which is a slug-like value that can be used on client applications to create friendly URLs.

Create Router Component

To manage the routing between the express-checkout-flow steps, you'll create a Router component that will render the correct step based on the cart's state and the step query parameter. So, create the file components/Router/index.tsx with the following content:

Directory structure after creating Router component file

tsx

tsxCopy
"use client"
import { useRouter, useSearchParams } from "next/navigation"
import { useCart } from "@/providers/cart"
import { useEffect, useMemo } from "react"
type ActiveTab = "product" | "address" | "shipping" | "payment"
type RouterProps = {
handle: string
}
export const Router = ({
handle,
}: RouterProps) => {
const { cart } = useCart()
const searchParams = useSearchParams()
const router = useRouter()
const currentStep = searchParams.get("step")
const isCartValid = useMemo(() => {
return cart?.items?.[0]?.product_handle === handle
}, [cart, handle])
const activeTab: ActiveTab = currentStep === "product" || currentStep === "address" ||
currentStep === "shipping" || currentStep === "payment" ? currentStep : "product"
useEffect(() => {
if (!cart) {
return
}
if ((activeTab !== "product") && !isCartValid) {
return router.push(`/${handle}`)
}
if (activeTab === "shipping" && (!cart?.shipping_address || !cart?.billing_address)) {
return router.push(`/${handle}?step=address`)
}
if (activeTab === "payment" && (
!cart?.shipping_address || !cart?.billing_address || !cart?.shipping_methods?.length
)) {
return router.push(`/${handle}?step=shipping`)
}
}, [isCartValid, activeTab])
return (
<>
{/* TODO render components */}
</>
)
}

You create a Router component that manages the routing between the product, address, shipping, and payment steps. The component uses the useCart hook to access the cart data, the useSearchParams hook to access the query parameters, and the useRouter hook to navigate between steps.

The different steps will be shown in the page as tabs or cards. The active tab is set to one of the following values:

  • product: The product selection step, which is the default step.
  • address: The shipping address details step. This step is set as active if the step query parameter is set to address and the cart has the selected product. This step is also set as active if the step query parameter is set to shipping or payment and the cart doesn't have shipping and billing addresses set.
  • shipping: The shipping method selection step. This step is set as active if the step query parameter is set to shipping and the cart has the selected product and the shipping and billing addresses set. This step is also set as active if the step query parameter is set to payment and the cart doesn't have a selected shipping method.
  • payment: The payment and cart completion step. This step is set as active if the step query parameter is set to payment and all previous steps have been completed.

You'll render the components for each step as you create them next.

Create Express Checkout Page

To create the page that shows the express checkout page, create the file app/[handle]/page.tsx with the following content:

Directory structure after creating express checkout page file

tsx

tsxCopy
import { Router } from "../../components/Router"
import { Suspense } from "react"
type Params = {
params: Promise<{ handle: string }>
}
export default async function ExpressCheckoutPage({
params,
}: Params) {
const handle = (await params).handle
return (
<Suspense fallback={<></>}>
<Router handle={handle} />
</Suspense>
)
}

In Next.js 15, any component that uses useSearchParams() must be wrapped in a <Suspense> boundary to enable static prerendering. Without the <Suspense> boundary, the build fails with: useSearchParams() should be wrapped in a suspense boundary.

This page will render the Router component, which will manage the routing between the product, address, shipping, and payment steps. You'll add the components for these steps next.


Next: 7. Product selection