Skip to content
All work
17Ordering experience2026

Soft Beans Palace

A Next.js ordering flow for a Port Harcourt food business — meal configuration, persisted cart, and checkout that hands off to WhatsApp.

3
Menu categories
0
Backend services
1
Source of truth for price
100%
Type-checked forms
Role
Designer & engineer
Year
2026
Status
Live
Type
Ordering experience

01Overview

Soft Beans Palace is a food business in Port Harcourt. This is its storefront: customers browse the menu, configure meals with sides and proteins, review the order and send it over WhatsApp.

It is the smallest project here and the one with the tightest constraints. No payment gateway, no backend, no accounts — the business confirms payment manually from a transfer receipt.

That made it purely an interface problem, which is why I like it. Everything the product does is in how it feels to use.

02The problem

The business already took orders on WhatsApp. Replacing that would have meant asking the owner to learn a dashboard and asking customers to trust a payment form. Both were the wrong trade.

So the site had to do the hard part — browsing, configuring, pricing, capturing details — and then hand a clean, complete, unambiguous order into the channel that already worked.

The configuration itself is deceptively awkward. A meal is a base dish plus any number of sides and proteins, each with its own quantity and its own instructions, and it has to stay legible on a phone.

03Architecture

  1. 01

    Next.js App Router

    TypeScript throughout, Tailwind for styling, shadcn/ui primitives on Base UI, Motion for animation that has a reason to exist, and Lucide for icons.

  2. 02

    Cart as persisted client state

    Zustand with localStorage persistence. A customer can configure four meals, close the tab, and come back to them.

  3. 03

    Focused configuration picker

    Sides and proteins open in a bottom sheet on mobile and a dialog on desktop — the same component, the right shape for the device. Each addition carries its own quantity.

  4. 04

    Single source of truth for the menu

    Dishes, descriptions, categories and numeric prices live in one data file. Prices flow to the menu, detail page, cart, checkout and the WhatsApp message through one currency formatter. No component ever hardcodes a formatted price.

  5. 05

    Validated checkout

    React Hook Form with Zod for customer, delivery and manual bank-transfer details, generating a unique reference per order — SBP-20260904-A82F7C — assigned once at submission.

  6. 06

    Configuration over hardcoding

    WhatsApp number, phone, socials, location, hours and bank details all read from environment variables through one site config, with Nigerian local-format numbers normalised into international format automatically.

04Key decisions

Decision 01

WhatsApp as the checkout target

Meeting the business and its customers where they already are beat introducing a payment gateway neither party had asked for. The site's job is to make the message perfect.

Decision 02

"Price on request" instead of invented prices

Prices were unconfirmed at build time, so the UI says so. Placeholder numbers on a live commerce site are a defect waiting for a customer to find.

Decision 03

One order reference, generated once

Generated at submission, not on render — so a re-render can never produce a second reference for the same order.

05Stack

  • Next.js
  • TypeScript
  • Tailwind CSS
  • shadcn/ui
  • Zustand
  • React Hook Form
  • Zod
  • Motion