Templates

How to Build a Restaurant Online Ordering System with Next.js

Build an online ordering system for a restaurant with Next.js: run the stack, add kitchens and a bilingual menu, test payments and go live.

By Aniq-UIOctober 18, 202627 min read
Share:
How to Build a Restaurant Online Ordering System with Next.js
#Next.js#restaurant#online ordering#NestJS#Stripe#Arabic RTL#Food Studio

By the end of this guide you will know how to build an online ordering system for a restaurant with Next.js: a public ordering website, a back office for the kitchen and the owner, and an API that holds the rules. We use the Food Studio template as the starting point and walk one example restaurant from first install to a live launch.

The aim is a plan you can follow, not a feature tour. You will see what to do on day one, which settings matter before you take a real payment, and which mistakes cost the most time. The same steps work for a single restaurant, a takeaway brand with several kitchens, or a site you build for a client.

The brief

Olive Street Kitchen is a small Mediterranean takeaway with two kitchens, one in the old town and one near the station. The owner wants guests to order from a phone, pay online, and follow the order until it arrives. Here is what the project has to do:

  • Show a menu that guests can search and filter by category and diet, with allergens on every dish.
  • Sell a combo (a main, a side and a drink) at one price.
  • Offer delivery and pickup, and only accept a delivery when the address is in a kitchen's area and the kitchen is open.
  • Take card or PayPal payments without the restaurant ever handling card numbers.
  • Give the kitchens a queue of paid orders, and keep prices hidden from kitchen staff.
  • Work in English and Arabic, with the Arabic pages mirrored right to left.
  • Let the owner change the menu and the home page without asking a developer.

How the template covers the brief

NeedWhere it livesWhat you use
Searchable menu with allergensOrdering websiteThe menu page at /en/shop, and the dish editor in the dashboard
Combo at one priceWebsite and dashboardThe combo page, and the combo offer and meal builder sections of the home page
Delivery rules per kitchenAPI and dashboardSettings, Restaurant: kitchens, hours, delivery postcodes, fees and minimum order
Online paymentAPIStripe or PayPal keys and webhooks in the API's .env file
Kitchen queue without pricesDashboardTeam page, the kitchen role tied to one kitchen
English and Arabic with RTLAll three apps/en and /ar routes, the messages folders, a bilingual dish editor
Owner edits the home pageDashboardHomepage, nine sections you switch, reorder and edit

Before you start: decisions to make

Most delays in a project like this come from decisions nobody made early. Settle these before you open a terminal.

  • Which package. A single-app package needs an API running somewhere, so choose one only if you already have it.
  • Where it runs. You need a Node host for three apps, a database (SQLite on a persistent disk, or MySQL) and a place for uploads (a persistent folder or a bucket such as Cloudflare R2).
  • Which languages. Every dish is stored in English and Arabic, so someone has to write or check each Arabic name and description before launch.
  • Which payment providers. Stripe, PayPal or both, on accounts that belong to the restaurant. Tips, tax lines and cash on delivery are not included.
  • Kitchens and delivery areas. Each kitchen needs an address, hours, a time zone, a preparation time and its delivery postcodes. Gather them on one sheet.
  • Who runs the dashboard. Decide the owner, the managers and one kitchen account per kitchen.
  • How guests hear about an order. There are no order confirmation emails, so guests rely on the tracking page. Decide whether that is enough for the first month.

Step by step

Step 1: Choose the package

Food Studio is sold as one full-stack template and also as separate pieces: the ordering website, the staff dashboard and the backend API. The website and the dashboard have no data of their own. They read everything from the API, so a website or dashboard package alone needs an API running somewhere.

For a new restaurant, take the Full Stack package. It holds all three apps, the database setup and the demo restaurant in one folder. If a client already has an API you trust, or you are only replacing one front end, a single package is enough. The pieces are listed on the ordering website and staff dashboard pages.

Check before moving on

You can name the ZIP you downloaded and the apps it holds. If it is a front end alone, you know where its API will run.

What usually goes wrong

A single front end shows nothing without an API, because neither app holds data of its own.

Step 2: Run it locally with Docker

Install Docker Desktop (or Docker Engine with Compose), download the ZIP from My templates, unzip it and open a terminal in the food-studio-full-stack folder. Then start everything with one command:

docker compose up --build

Nothing needs configuring first. The first run builds three images and loads a demo restaurant, so allow a few minutes. When it settles, you have three apps:

  • The ordering website at http://localhost:3030 (Arabic at /ar).
  • The staff dashboard at http://localhost:3031.
  • The API at http://localhost:8000, with every route under /api.

Food Studio install video still: starting the API, ordering website and staff dashboard together with Docker

In this still from the install video, look at the single command that starts all three apps together.

If another program already holds one of those ports, put a .env file beside docker-compose.yml with SITE_PORT, ADMIN_PORT or API_PORT set to a free number, then run the command again. The other addresses follow by themselves.

Sign in to the dashboard as the owner with owner@foodstudio.example and the password FoodDemo2026!. The same password works for manager@foodstudio.example and kitchen@foodstudio.example. On the website, the demo customer is sam@foodstudio.example. These are local demo accounts, so plan to change them before the site is public.

Spend twenty minutes here as three different people: order as a guest, move the order along as the owner, then open the kitchen account and see how little it shows.

Food Studio tour video still: opening a dish and adding it to the cart

Look at how a dish opens with its quantity and instructions before it goes into the cart.

Food Studio restaurant template menu page with search, category, dietary and sort filters in the light theme

To stop the apps, press Ctrl+C or run docker compose down. Your data is kept in the food-studio-data volume. Be careful with docker compose down -v: it also deletes that volume, and the next start seeds a fresh demo restaurant over everything you entered.

Check before moving on

All three addresses answer, a guest order reaches the dashboard, and the kitchen account shows no prices.

What usually goes wrong

Docker is not running, so start Docker Desktop and retry. A busy port ends the start with "address already in use": move the app with the port variables above. A start that looks stuck is usually still seeding. Without Docker, an old Node.js (the apps expect 24 or newer) or Yarn 1.22 (run corepack enable) is the usual cause.

Step 3: Set the business details

Do the name and look before the menu, so every later screenshot is the real brand. Three places matter:

  • storefront/src/config/brand.config.ts holds the names (an English and an Arabic value each), the domain, the contact details and the social links.
  • storefront/src/styles/theme-variables.css holds the colors, with a light value and a dark value for each. Change --primaryColor and --heading-red and every page follows. Check that text on the new color is still readable in both themes.
  • storefront/public/brand/ holds the logo files, one per language and theme.

The restaurant name, its tagline and its support contact are stored as settings, so the owner can change them later from the All settings by key list in the dashboard (Settings, then Restaurant). Also open messages/legal: the privacy policy and terms in the download are written for a fictional restaurant. Replace them with texts that fit Olive Street Kitchen and its country before launch.

Check before moving on

The new name appears in tab titles, the footer and headings in both languages, and text on your brand color stays readable in both themes (the docs ask for a 4.5 to 1 contrast ratio).

What usually goes wrong

A color changed in one theme only, or English copy rewritten without the matching line in ar.json.

Step 4: Add the kitchens, hours and fees

Delivery rules are data, not code. In the dashboard, open Settings and the Restaurant tab. For each kitchen you set the address, the opening hours and time zone, the preparation time, the postcodes it delivers to and a spot on the map. The demo has four kitchens. Olive Street Kitchen keeps two and edits them into the old town and station kitchens, then turns off the rest.

The same tab holds the fees for delivery and pickup, plus the minimum order. The demo starts with a $2.99 delivery fee, free pickup and a $10.00 delivery minimum, so treat those as placeholders for your own numbers. Also look at the service clock. The seed starts on the Demo clock, which holds every kitchen open at 20:55 so you can test at any hour. Switch it to Real before launch, or guests will be told you are open at 3 a.m.

Food Studio staff dashboard settings page with kitchens pinned on a map

Check before moving on

Use the postcode search on the locations page twice, once with a postcode in a kitchen's list and once outside every list, and read both answers.

What usually goes wrong

Checkout refuses a guest, claiming the kitchen is shut or the delivery address is out of range. The cause is nearly always data: a missing postcode, hours in the wrong time zone, or a basket under the minimum. Fix it in the kitchen's settings.

Step 5: Build the menu in two languages

Open Menu in the dashboard. Every dish carries its name, text and ingredient list in both languages, typed next to each other. Add a category, a preparation time, allergens, dietary tags and photos. Extras and sizes are option groups: each group has its own choices, each choice can move the price, and you set how many a guest must and may pick. Optional stock makes the server refuse an order it cannot fill.

For our example we create a few clearly fake dishes first: a lamb plate, a falafel wrap, a hummus starter, a side salad and a lemonade. Then we set up one combo with a choice of main, side and drink. A guest who swaps in a pricier choice pays only the difference. Finish by opening the Homepage page and choosing which of the nine sections to show, in what order, and with which popular dishes.

Food Studio combo page with a choice of main, side and drink, light theme

Look at the choice offered for each course and the price shown for the combo as a whole.

Food Studio dish editor with English and Arabic fields side by side in the light theme

Check before moving on

Open one dish in English and in Arabic, mark one dish unavailable (it stays visible and says so), and order the combo with a pricier side to confirm that only the difference is added.

What usually goes wrong

A dish that never appears is unpublished or archived. Photos that fail to load point to PUBLIC_MEDIA_URL, which the seed writes into every picture address, so on a server set it before you seed for the first time.

Step 6: Set up payments in test mode

With no payment provider configured, checkout offers a built-in demo payment that skips real money and marks the order paid. It is good for learning the flow, and it disappears once a real provider is set. Guests pay on Stripe's or PayPal's own page, so your site never sees a card number.

For Stripe, put a test secret key (it starts with sk_test_) in the API's back-end/.env, then add a webhook endpoint in Stripe that points at your API followed by:

/api/payments/webhooks/stripe

Subscribe it to checkout.session.completed, checkout.session.expired, charge.refunded and charge.dispute.created, copy its signing secret into STRIPE_WEBHOOK_SECRET, and restart the API. For PayPal, set PAYPAL_CLIENT_ID, PAYPAL_CLIENT_SECRET and PAYPAL_WEBHOOK_ID; PAYPAL_ENV is sandbox by default, which moves no money.

Then place a test order and watch it. An order is saved as unpaid, and it counts as paid only after the API has asked the provider what happened. If a customer abandons the payment page, the API cancels the order later and gives the stock and reward points back. The server prices every order from its own menu, so a browser cannot change a total.

Food Studio checkout page with contact details, delivery and hosted payment, light theme

Look at the payment choice on the checkout: the guest is sent to the provider's own page, so no card field appears here.

Check before moving on

An order is saved unpaid, turns paid after the provider confirms, and then shows in the kitchen queue as confirmed.

What usually goes wrong

A paid order stays unpaid. Check the webhook address, the signing secret in the API's .env, a restart of the API, and that API_PUBLIC_URL and STOREFRONT_URL are reachable from the guest's browser. The guest returning to your site never pays an order by itself.

Step 7: Add staff and roles

Open Team. The template has three kinds of account. The owner sees everything, including the team and the roles. A manager sees everything except those two. A kitchen account is limited to one kitchen's orders, hides prices and the customer's email, and moves orders from confirmed to preparing to ready.

For Olive Street Kitchen, create one kitchen account for each of the two kitchens, plus a manager. Roles are edited permission by permission, so you can also build a role of your own. Then delete or change the demo accounts. Nobody should be able to sign in with a password printed in a document.

Food Studio staff dashboard team page with roles and kitchen assignments

Check before moving on

The kitchen account sees only its own kitchen, with no prices, and can move orders to ready. The manager does not see the Team page.

What usually goes wrong

Only the owner holds the team and role permissions, so a manager cannot fix a role. Edit roles permission by permission when the defaults do not fit.

Step 8: Decide whether you want the AI tools

The AI features are optional, and the restaurant runs without them. With a Google Gemini key in the API's .env, three things switch on: an assistant you can ask about orders, sales and the menu, which answers from your own data, a writer that drafts a dish listing from a prompt or photos, and a photo restyle tool. Google bills for Gemini use under its own pricing, so check the Gemini pricing page before you give a key to a restaurant owner.

One tool costs nothing per photo: removing a dish photo's background runs on your own server with a local model and needs no key. You only add the model file. That is what lets each dish sit cut out on its own tile. If you skip the AI keys, those buttons say they are not connected, and nothing else breaks.

Food Studio AI studio restyling a dish photo with a before and after comparison

Look at the dish photo before and after restyling, with the original kept for comparison.

Check before moving on

Ask the assistant something you can verify against the overview, such as how many orders came in today, and read the before and after of any change it proposes.

What usually goes wrong

The AI features say they are not connected. Put the key in the API's .env, never in a front end file, and restart the API.

Step 9: Test the whole order, on a phone and in Arabic

Before you touch a domain, run the order from end to end. On your phone, open the site, find a dish, add a combo, pick delivery, enter a postcode inside a kitchen's area, and pay with the test key. Watch the order appear in the dashboard, move it through each status and confirm the guest's tracking page updates. Pickup orders skip the delivery step.

Food Studio cart with the delivery or pickup choice and a promo code field, light theme

Look at the delivery or pickup switch at the top of the cart, which decides whether the postcode check applies.

Then repeat the run in Arabic at /ar. Check the menu, the cart, the checkout and the dashboard in the right to left layout. Also check an address outside every kitchen's area and an order placed while a kitchen is closed, because those are the cases a real guest will hit on day one.

Food Studio order tracking page showing the order being prepared, light theme

Look at the status line that tells a guest where the order is, which is the page you should refresh while you move the order in the dashboard.

Food Studio dashboard orders page with the timeline of the selected order

Food Studio Arabic menu page laid out right to left, light theme

Look at the mirrored layout: filters, tiles and navigation switch sides while prices keep Latin digits.

Food Studio dish page on a phone, light theme

Look at how the dish page fits a narrow screen, with the quantity and the add button within reach of a thumb.

Check before moving on

Run your test list on a phone and a laptop: find a dish, switch delivery to pickup, apply a promo code, pay, and watch the timeline refresh.

What usually goes wrong

New orders show up only after a reload. The dashboard connects to NEXT_PUBLIC_WEBSOCKET_BASE_URL, and the API accepts that connection only from the address in FRONTEND_URL.

Step 10: Go live

Going live means three apps on a Node host, a database, a place for uploads and your real domains. The template keeps its decisions in .env files, so most of the work is setting them correctly:

  • Set NODE_ENV=production and your own JWT_SECRET. Generate a fresh secret rather than reusing the example. In production the API will not start without CORS_ORIGIN, which lists the website and dashboard addresses, comma separated.
  • Set FRONTEND_URL (the dashboard, for live order notifications), STOREFRONT_URL (where payments and password resets send the guest) and API_PUBLIC_URL (where Stripe and PayPal return first).
  • Choose the database: SQLite on a persistent disk, or an empty MySQL database with its connection set in .env and the seed or table setup run before the first start.
  • Point uploads at persistent storage: a local folder on a disk that persists, or a bucket (set all five bucket variables).
  • In both front ends, set NEXT_PUBLIC_API_BASE_URL to the live API address including /api, and rebuild. These values are compiled into the code the browser loads, so a change does nothing until the next build.
  • Switch Stripe and PayPal from test keys to live keys, and add the live webhooks.

Do not use yarn railway:setup as the build command of a live restaurant. It drops every table and seeds again each time it runs, which suits a new environment and ruins a live one. Then place one small real order and refund it from the provider's own dashboard, so you know the whole loop works with real money.

Check before moving on

In a private window, place a guest order and confirm the payment page, the return to your site and the dashboard notification all work.

What usually goes wrong

Empty pages with a CORS message in the console mean the real addresses are missing from CORS_ORIGIN. "Too many attempts" for everyone means TRUST_PROXY is unset behind a proxy. Photos fail when NEXT_PUBLIC_MEDIA_HOSTNAME does not name their host.

Running it day to day

Launch is the start of the work. Here is a normal day and week for the Olive Street Kitchen team, using screens the dashboard already has.

A morning in the dashboard

The manager opens the overview. It opens on the money and the pace of the day (revenue, order count, average order, orders being prepared) set beside yesterday, then charts revenue by hour and category mix, and lists the best sellers and the kitchen queue. It refreshes when an order lands.

Food Studio staff dashboard overview with the day's sales, the kitchen queue and popular dishes

Look at the kitchen queue beside the sales figures: it is the part of the screen the team returns to all day.

Next comes the orders page. Filter by status, delivery or pickup, or date, then open an order for its items, totals and timeline. Only the next sensible status is offered, and a cancellation needs a reason. Check the bell too: it holds contact form messages.

During service

Kitchen staff work from their own account, see confirmed orders for their kitchen without prices, and move them along. When something runs out, the manager switches that dish to unavailable on its menu row, which stops new orders without deleting the dish.

Weekly tasks

  • Refresh the home page. Reorder the nine sections, edit the hero in both languages and pick the popular dishes. No code change is needed.

Food Studio dashboard homepage editor listing the nine home page sections

Look at the list of sections with their switches and arrows, which is how the order of the public home page is set.

  • Review the menu. Archive what you no longer cook and add the seasonal dish with both languages filled in.
  • Ask the assistant. Which dishes sell best this week? It answers from your own data and draws a chart for trends.
  • Back up the data. The database and the uploads are the business. Switch on your host's snapshots and keep a copy elsewhere.

An example launch plan, week by week

This is an example schedule for Olive Street Kitchen, not a promise about how long your project takes.

  • Week 1, foundations. Run the stack with Docker, take the tour as owner, manager, kitchen and customer, make the decisions above, and start the branding and legal pages.
  • Week 2, content. Enter both kitchens, the categories and the first dishes in both languages with photos. Build one combo and arrange the home page.
  • Week 3, money and people. Connect Stripe and PayPal in test mode, create the team accounts, and run the full order path on two phones and in Arabic.
  • Week 4, go live. Deploy on your domains, switch to live keys, place one small real order, and invite a few regular customers first.

Launch checklist

  • The restaurant name, contact details and legal texts are yours, in English and Arabic.
  • Every dish on the menu has both languages, allergens and a photo.
  • Every kitchen has the correct hours, time zone, postcodes, fees and minimum order.
  • The service clock is set to Real, not the demo clock.
  • The demo accounts are deleted or have new passwords.
  • JWT_SECRET is your own, and NODE_ENV is production.
  • CORS_ORIGIN, FRONTEND_URL, STOREFRONT_URL and API_PUBLIC_URL use the live addresses.
  • Both front ends were rebuilt with the live API address.
  • Stripe and PayPal use live keys, and their live webhooks deliver.
  • The database and the uploads live on storage that survives a restart, and a backup exists.
  • domain.url in the brand config is the real address.
  • One real order was placed, tracked and refunded from the provider's dashboard.

Template, from scratch or a hosted platform

There are three honest ways to put a restaurant online, and none is best for everyone.

A template like this one

You get the source for the website, the dashboard and the API, so you own the result and can change anything. Server-side pricing, delivery checks, order statuses and payment webhooks are already solved. The cost is that you host and maintain it, and anything the template lacks, such as emailed confirmations, is development work.

Building from scratch

You get exactly the product you design. You also build and test every rule yourself: how a combo prices, how a webhook confirms a payment once, how stock is held and released. For most restaurants that is a long road to where a template starts.

A hosted ordering platform

This is the fastest way to start, and someone else runs the servers. The trade-offs are recurring fees, less control over the guest experience, and your order data living on their system. Compare each platform's terms, payment fees and Arabic support before choosing.

What it costs to run

The template is a one-time purchase, from $79 for the ordering website alone. The Full Stack package we used in this example is $179. It includes lifetime support and a commercial license.

Running the restaurant costs more than the template. You pay for:

  • Hosting for the API, the website and the dashboard, and a persistent disk or a MySQL database.
  • Storage for photo uploads if you choose a bucket such as Cloudflare R2.
  • Payment processor fees. Stripe and PayPal charge per their own pricing pages, and the template does not add to them. See Stripe pricing.
  • A Google Gemini key, only if you turn on the AI assistant or the photo tools.
  • Your own domain.

What costs nothing: local development on SQLite, the demo payment, the map tiles (the template defaults to OpenFreeMap's public styles, which need no key) and background removal on your own server.

What to customise next

  • Rewards. The demo gives one point per dollar and takes $5 off for 100 points. Change those numbers in Settings to fit your margins.
  • Promo codes. Create a code with a percentage, decide whether it covers the menu or the whole order, and whether it combines with rewards.
  • The home page. Reorder the nine sections to match a campaign, and rewrite the hero in both languages without a code change.

Common mistakes

Changing a front end's .env and expecting it to apply

Every NEXT_PUBLIC_* value is compiled into the page at build time. If the site still calls the old API after you edit the file, rebuild it. With Docker Compose, put the value in the .env beside docker-compose.yml and run docker compose up --build.

Leaving the demo clock on

The seed keeps every kitchen open at the same fixed time so you can test at midnight. If you forget to switch to the real clock, guests can order from a closed kitchen.

Forgetting CORS on the live domains

If the live pages stay empty and the browser console mentions CORS, the API does not know your domain yet. Add both front end addresses to CORS_ORIGIN and restart the API.

Assuming features that are not included

The template does not send order confirmation emails, take orders for a later time, add tips or tax lines, or handle cash on delivery. Password reset mail goes through Resend if you configure it. If Olive Street Kitchen needs any of the others, budget development time for them.

FAQ

How do I build an online ordering system for a restaurant with Next.js?

Start from a template that already includes the hard parts, then configure it. You need a customer website, a staff dashboard and an API that prices orders and talks to a payment provider. This guide walks through the steps with Food Studio: run it, brand it, enter kitchens and the menu, test payments, add staff and deploy.

Do I need a backend?

Yes. Prices, stock, order statuses and payment confirmation must be decided on a server, not in the browser. The template ships a NestJS API with TypeORM for that, and the website and dashboard read everything from it.

Can I use it for client projects?

Yes. It comes with a commercial license for unlimited projects, which covers agencies building restaurant sites. Read the license in the documentation for the exact terms.

Does it support Arabic and RTL?

Yes. The website, the dashboard and the API run in English and Arabic. Arabic pages mirror to right to left, and dishes are stored with a name and description in both languages.

Can I run several kitchens?

Yes. Each kitchen has its own hours, time zone, delivery postcodes and staff accounts. The website tells a guest if a kitchen delivers to their postcode, is closed, or if another kitchen can take the order.

Which payment providers work?

Stripe Checkout and PayPal. Both take the guest to their own page and confirm the order through a signed webhook. You use your own accounts, and their fees are not part of the template price.

Do I need Docker?

No, but it is the quickest start. Without Docker you need Node.js 24 or newer and Yarn through Corepack, and you run each app with yarn dev. The API needs no database server, because it uses SQLite by default.

Can guests order without an account?

Yes. Guests can check out with a name and an email, and track the order by its number and that email. An account adds reward points, saved dishes and addresses, and a reorder button.

Does it send order confirmation emails?

No. The template does not send order confirmation emails. Guests see the order on the confirmation and tracking pages, and the dashboard notifies the team. Password reset mail works through Resend once you add its keys.

Can I use MySQL instead of SQLite?

Yes, as a configuration change. Create an empty database, set the driver and connection in the API's .env, and run the seed or the table setup before the first start in production.

Try it yourself

Order from the live ordering website, then open the staff dashboard demo to see the other side. Read the Food Studio documentation for every setting in this guide, or see the product page for the packages. More options are in the restaurant template category. Food Studio is also one of ten templates in the Next.js Templates Bundle.

Found this article helpful?

Share:
Newsletter

Get notified when a new template ships

Be the first to know when a new template is released.

  • New template releases
  • See what is being built and vote on it
  • Free and premium releases
  • Unsubscribe in one click
world map

Global User Community

Join thousands of developers worldwide who trust Aniq-UI for their projects. Our templates are being used across the globe to create stunning web experiences.

Aniq-uiAniq-ui

Cookie Preferences

We use cookies to enhance your browsing experience, analyze site traffic, and personalize content. By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising.