Tenant architecture
Multi-language support
In production
The project and the problem.
Multi-tenant menu management platform helping restaurants showcase menus, attract customers, and manage operations from one dashboard.
Restaurants needed a single platform to manage menus, customer-facing pages, and growth tooling without requiring technical staff. The product had to scale across multiple locations and languages from day one.
- Let non-technical restaurant owners manage their own menus
- Support multiple locations and languages from day one
- Give every restaurant a branded, mobile-first customer menu
The approach and process.
We architected Dineri as a multi-tenant SaaS so each restaurant gets an isolated, fully branded experience while running on shared infrastructure.
- 01
Sitting in Restaurants
2 weeksWe spent the first two weeks talking to real restaurant owners and watching how they actually scanned QR codes at their own tables. Two things became obvious fast: every owner spoke a different language, and almost none of them knew what "multi-tenant" meant. But they all instantly understood "your menu, your colors, our problem."
Owner interviews (8 restaurants, 3 countries) · Tenant isolation model on paper · i18n strategy. Translatable per dish, not just per shell · A rough roadmap the client could read without us
- 02
Designing the Menu, Not the Dashboard
3 weeksWe designed the customer menu first, before the admin. Most SaaS does this backwards. The whole product exists to make the menu great. So we got the menu right, then worked backward to the dashboard that produces it.
Customer menu in 6 brand variants · Owner dashboard built around the menu, not vice versa · Design tokens for per-restaurant theming · Mobile interactions tested on real phones at restaurant lighting
- 03
The Multi-tenant Bones
4 weeksWe made the tenancy boundary the first thing the code knows about. Every query takes a tenant ID before it takes anything else. We caught two early bugs in code review where a developer forgot to pass it. And made it a compile-time error after that.
Subdomain routing with tenant context propagated everywhere · Theming engine (CSS variables resolved at runtime per tenant) · Auth + onboarding wired to the new tenant flow · Type-level guarantee that tenant ID can't be forgotten
- 04
Menu Builder + i18n
3 weeksThe menu builder went through three rewrites. The first was technically clean but felt fiddly. The second was easy to use but skipped translations. The third. The one that shipped. Let owners switch languages while editing and see the customer view update in a panel beside them.
Drag-and-drop menu builder (third attempt, charmed) · Per-dish translation editor with live preview · Image uploads with client-side compression · Schema validation that prevents broken menus going live
- 05
Quiet Launch
2 weeksWe launched with the first three restaurants from the original interviews. One of them onboarded entirely on their own while we were on a call with another. That was the moment we knew the flow worked. Analytics and monitoring went up the same week.
Production launch at dineri.app · Self-serve onboarding (tested by a non-engineer in 11 minutes) · Per-restaurant analytics on the dashboard · Short owner-facing runbook. 4 pages, not 40
The reasoning and the value.
We built Dineri so a restaurant owner with no tech background can open their phone at the table, change the price of a dish, and have it live before the customer puts the menu down.
Dineri is live in production at dineri.app, supporting multiple restaurant tenants across languages and locations with a single owner dashboard.
Multi-tenant architecture
Each restaurant runs on isolated, branded surfaces while sharing infrastructure onboard a new tenant in minutes, not days.
Internationalization (i18n)
Customer menus and the owner dashboard fully translatable. Restaurants in any country, any language, from day one.
Per-restaurant theming
Owners can customize colors, logos, and brand voice every customer-facing page reflects the restaurant, not Dineri.
Owner dashboard
Menu builder, location management, analytics, and customer insights all in one non-technical-friendly admin.
Mobile-first menu pages
Optimized for phone scanning at the table fast loads, accessible touch targets, image-rich layouts.
Live in production
Running today at dineri.app supporting real restaurants.
What changed.
A side-by-side of what the client had before we engaged, and what we shipped.
- Most restaurants we talked to were emailing a PDF menu to a Linktree page, or worse, asking a cousin to update a printed flyer
- Multi-location owners maintained the same menu in three places, and at least one was always wrong
- Changing the price of a single dish meant a phone call, a Photoshop edit, and a re-upload. Sometimes a day's wait
- Customers landed on the menu, pinch-zoomed a PDF, gave up, and asked the waiter
- Owners had no idea which dishes people actually looked at
- Staff could not update anything without help from the friend who originally set it up
- Nothing across the menu, the social links, or the booking page looked like the same restaurant
- Each restaurant gets its own subdomain, its own colors, its own logo. Sharing one quiet platform underneath
- One dashboard, every location, every menu. Including the one in the second city the owner kept forgetting to update
- Edit a dish, hit save, and the customer scanning the QR code at table 4 sees the new price on their next refresh
- Customer menus designed for one-handed scrolling on a phone at the table, not a desktop browser
- Owners can see exactly which dishes get viewed, which get skipped, and which photos people actually open
- The onboarding flow is short enough that the owner finishes it themselves before we get on the call
- Customer-facing pages feel like the restaurant. Same fonts, same colors, same warmth. Not like Dineri
Technology stack.
The tools behind the build, and the reasoning behind each choice.
We needed per-tenant theming at render time. Passing one theme object down the tree was simpler than fighting a framework about it.
Made tenant ID a non-optional argument at the type level. Two bugs caught at compile time that would have caused tenants to see each other's data.
Theming via CSS variables wired into Tailwind tokens. Owners change their primary color in a picker, the entire UI follows, no rebuild.
Each restaurant gets its own host. The router resolves the tenant before any data fetch. No "oops, wrong restaurant" possible.
Stored per tenant, injected as CSS variables. Falls back to safe defaults if an owner clears a value mid-edit.
Most i18n libraries translate the shell, not the content. We needed the content too. A paella in Madrid and London are not always described the same way.
Customer's language choice sticks across visits via a cookie. The owner can preview every translation before publishing.
We designed the customer menu before the dashboard, then matched the dashboard to it. The whole platform exists to serve that one screen.



