All projects Online ordering

BestellApp

The whole path a customer walks to place an order: a menu, a cart that adds up as you go, a delivery fee that changes with the total, checkout and a confirmation. The backbone under every shop and delivery service.

My role
Full rewrite — architecture, design, code
Size
~2,000 lines, 20 menu items
Stack
React 18, Vite, Tailwind
Status
Live, two languages
The BestellApp menu with a cart open

Why an ordering flow is worth building carefully

Everything before the cart is design work. Everything after it is money. A visitor who cannot see their running total, loses the cart on a page refresh, or reaches a checkout form that asks for the same thing twice, does not complain — they leave, and nothing in the analytics tells you why.

So this project is deliberately about the unglamorous middle: what a cart is, where it lives, and what happens between “I want this” and “order placed”.

The cart decision that saves trouble later

The cart is stored as a plain map of item id to quantity, and nothing else. Not a copy of the item, not its price, not its position in the menu — just { id: quantity }, kept in the browser between visits.

That sounds like a detail and behaves like insurance. Prices change, dishes get renamed, the menu gets reordered — and a cart saved yesterday still resolves correctly today, because it never stored a snapshot of anything that can change. Carts that copy the whole item are how customers end up seeing last month's price at checkout.

Store the reference, resolve the rest at render time. It costs nothing while you are building and saves an awkward conversation once real prices start moving.

Totals are derived rather than stored, too. Quantity, subtotal and item count are all computed from that one map, so there is no second source of truth to drift out of sync with the first.

Delivery that rewards a bigger order

Delivery is €3.90, and free above €25. Both numbers sit in one place in the code, next to each other, so a client can change them without anyone hunting through components.

The interesting part is not the fee, it is that the customer can see where they stand relative to the threshold while they are still choosing. That is the oldest lever in online ordering, and it works for a plain reason: “add €4 more and delivery is free” is a better offer than “pay €3.90”, and both sides come out ahead.

Two languages, decided at the start

The interface runs in German and English, and every string lives in one translation file rather than inside the components. Menu item names are kept separately again, in the menu data, because they belong to the client's content and not to the interface.

The reason to do this on day one is cost. Retrofitting a second language means opening every component in the project and pulling text out of it by hand; deciding it up front makes a new language a single file. Any client who might sell across a border will eventually ask for this, and it is far cheaper before launch than after.

Formatting follows the language too — prices are rendered per locale rather than glued together with a hard-coded symbol.

What is real and what is a demo

Being exact about this matters more than a good screenshot: