Skip to content

Hierarchical Checkout

A multi-step checkout flow covering cart, shipping, and payment. The payment step is complex enough to warrant its own sub-states — making this a natural fit for a nested submachine. The flattened variant shows what the same flow looks like when every state is promoted to the top level.

PaymentproceedbackproceedbackproceedchangePaymentbackexitchild.exitbackchangePaymentsubmitOrderrestartCartShippingShippingPaidMethodEntryAuthorizingAuthChallengeAuthorizationErrorAuthorizedReviewConfirmation
1
Cart
2
Shipping
3
Payment
4
Review
5
Done

Wireless Headphones

$99.99 each

$99.99

Bluetooth Speaker

$49.99 each

$99.98
Total$199.97
import {
createMachine,
defineStates,
effect,
setup,
withReset,
matchina,
} from "matchina";
import { submachine, nestedHsmRoot } from "matchina/hsm";
// Hierarchical checkout: main flow contains a payment submachine
export const paymentStates = defineStates({
MethodEntry: undefined,
Authorizing: undefined,
AuthChallenge: undefined,
AuthorizationError: undefined,
Authorized: { final: true },
});
// Create payment machine factory
function createPayment() {
const m = matchina(
paymentStates,
{
MethodEntry: {
authorize: "Authorizing",
exit: "MethodEntry", // Exit resets to initial state
},
Authorizing: {
authRequired: "AuthChallenge",
authSucceeded: "Authorized",
authFailed: "AuthorizationError",
exit: "MethodEntry", // Exit from any payment state goes back to MethodEntry
},
AuthChallenge: {
authSucceeded: "Authorized",
authFailed: "AuthorizationError",
exit: "MethodEntry",
},
AuthorizationError: {
retry: "MethodEntry",
exit: "MethodEntry",
},
Authorized: {
exit: "MethodEntry",
},
},
paymentStates.MethodEntry()
);
return withReset(nestedHsmRoot(m), paymentStates.MethodEntry());
}
const paymentFactory = submachine(createPayment, { id: "payment" });
const checkoutStates = defineStates({
Cart: undefined,
Shipping: undefined,
ShippingPaid: undefined,
Payment: paymentFactory,
Review: undefined,
Confirmation: undefined,
});
export function createCheckoutMachine() {
const checkout = createMachine(
checkoutStates,
{
Cart: { proceed: "Shipping" },
Shipping: {
back: "Cart",
proceed: "Payment",
},
Payment: {
back: "Shipping",
exit: "Shipping",
"child.exit": "Review",
},
Review: {
back: "ShippingPaid",
changePayment: "Payment",
submitOrder: "Confirmation",
},
ShippingPaid: {
back: "Cart",
proceed: "Review",
changePayment: "Payment",
},
Confirmation: { restart: "Cart" },
},
"Cart"
);
const hierarchical = nestedHsmRoot(checkout);
// Get payment machine from state to wire up reset effect
const getPayment = () => {
const state = hierarchical.getState();
return state.is("Payment") ? state.data.machine : null;
};
setup(hierarchical)(
effect((ev) => {
if (ev.type === "restart") {
const payment = getPayment();
if (payment) {
payment.reset!();
}
return true;
}
})
);
return hierarchical;
}
// Type export for payment machine (used by CheckoutViewNested context)
export type PaymentMachine = ReturnType<typeof createPayment>;
proceedbackproceedbackchild.exitbackchangePaymentsubmitOrderbackproceedchangePaymentrestartCartShippingPaymentReviewShippingPaidConfirmation
1
Cart
2
Shipping
3
Payment
4
Review
5
Done

Wireless Headphones

$99.99 each

$99.99

Bluetooth Speaker

$49.99 each

$99.98
Total$199.97
import { createHSM } from "matchina/hsm";
export function createFlatCheckoutMachine() {
return createHSM({
initial: "Cart",
states: {
Cart: {
on: { proceed: "Shipping" }
},
Shipping: {
on: {
back: "Cart",
proceed: "Payment"
}
},
Payment: {
initial: "MethodEntry",
states: {
MethodEntry: {
on: { authorize: "Authorizing" }
},
Authorizing: {
on: {
authRequired: "AuthChallenge",
authSucceeded: "Authorized",
authFailed: "AuthorizationError"
}
},
AuthChallenge: {
on: {
authSucceeded: "Authorized",
authFailed: "AuthorizationError"
}
},
AuthorizationError: {
on: { retry: "MethodEntry" }
},
Authorized: {
// Final payment state - child.exit automatically triggered
}
},
on: {
back: "Shipping",
"child.exit": "Review"
}
},
Review: {
on: {
back: "ShippingPaid",
changePayment: "Payment",
submitOrder: "Confirmation"
}
},
ShippingPaid: {
on: {
back: "Cart",
proceed: "Review",
changePayment: "Payment"
}
},
Confirmation: {
on: { restart: "Cart" }
}
}
});
}

In the nested variant, the Payment state contains a submachine (CardEntry → Processing → Confirmed / Failed). The outer machine delegates control to the submachine while payment is in progress and resumes once it reaches a terminal state. This keeps the top-level machine focused on the checkout stages and isolates payment complexity.

In the flattened variant, all payment sub-states are peers at the top level alongside Cart, Shipping, and OrderComplete. Every state is explicit and directly traversable, at the cost of a larger flat list of states and repeated transitions.

Submachines shine when a phase of your flow is complex enough to warrant local state management — and when you want to reuse or test that phase in isolation.