Wayfair: Making a Revenue-Critical Flow Configurable in Checkout

Wayfair's Block Builder is a server-driven UI platform. Instead of hardcoding what a page renders, the frontend asks Block Builder for a typed content graph and renders whatever comes back. Content editors configure it in a CMS.
The interactive builder below shows that model in practice: assemble a checkout screen, rearrange its layout, and inspect the typed GraphQL graph underneath.
Why HFC existed
High Friction Checkout (HFC) drove a 17% lift in Rewards attach rate during testing. Checkout continues to account for roughly 80-90% of Rewards acquisition.
Migration
The legacy component received one PHP data blob containing everything it needed: live checkout facts such as potentialRewards and isLoyaltySkuAdded, alongside titles, button labels, image IDs, and screen content. React consumed the whole shape as one unit.
I could have recreated that payload in GraphQL and called the migration finished. That would have changed the stack without changing the design. Copy, layout, checkout state, and behavior would still be tangled together.
Instead, I split the flow by ownership:
- Block Builder owns the description: screens, copy, terms, calls to action, layout, and styling tokens.
- The HFC component owns the interaction: selected choice, terms acceptance, loading and error state, and the current screen.
- Checkout owns the consequences: cart facts, the membership SKU mutation, and the global veil that can block the page.
CMS configuration
Block Builder returns a typed GraphQL graph for the full experience. Each screen has three regions (header, content, footer), each accepting a bounded set of content block types. Editors can change ordering, layout direction, spacing, and alignment through Homebase design tokens, but the schema prevents a payment action from landing in the header or arbitrary CSS from entering checkout.
The same component serves five locales. It selects one of four message tiers based on the customer's potential rewards, so a customer expected to earn $50 sees different copy from one expected to earn $2. The frontend does not need conditional copy for each tier. CMS strings can include placeholders such as [PotentialRewards] and [CartTotal], which resolve against live checkout data at render time.
Interaction modes
HFC runs in two modes: addToCart (terms-gated, mutates checkout) and standalone (no gate, no mutation).
The obvious design was a mode field. I didn't add one. A flag can disagree with the content around it. Instead, deriveExperienceType() reads the DEFAULT screen: if it contains a valid terms block, the mode is addToCart; if not, standalone. The schema carries the intent. There's no second flag for an editor to keep aligned.
If the configuration is incomplete, the component falls back to standalone rather than treating an ungated action as permission to mutate checkout.
Screen transitions
The CMS owns what each screen looks like. Transitions stay in a useReducer:
DEFAULT
├─ accepts terms, enrollment succeeds → REWARDS_ADDED
└─ declines → REWARDS_DECLINED
└─ reconsiders → DEFAULTThe membership SKU mutation also stays in code. Editors can change the call-to-action label, but not which SKU gets added to the cart.
Failure handling
HFC doesn't control the veil directly. It emits onVeilChange, and the checkout section translates that into global state actions. If HFC hits a render error, its error boundary asks checkout to disable the veil and remove the component so the customer can continue.
Content fallbacks work the same way. If a screen is missing copy for the current reward tier, the component falls back to that screen's default content and logs the gap. A content mistake can produce generic copy; it shouldn't block the payment page.
After the migration
Before the migration, changing HFC copy, layout, or screen content meant changing a checkout-specific payload and shipping code. Live checkout facts and presentation were part of the same contract.
Afterward, content editors could configure the experience across five locales without a frontend deploy. The HFC component rendered and validated that configuration, keeping terms validation and screen transitions in code. Checkout retained control of the membership SKU mutation and veil recovery.
The CMS describes the experience and expresses its intent. Code validates that intent, controls mutations to the customer's order, and decides how checkout recovers from a failure.