Build a zero-commission food delivery app using React and WhatsApp. Eliminate third-party aggregator fees with direct customer ordering.
Food delivery aggregators like Zomato, Swiggy, and Uber Eats charge restaurants between 22% and 35% in commission fees on every order. For an independent restaurant or cloud kitchen with average net operating margins of 12% to 18%, this fee structure destroys profitability unless menu prices are inflated significantly on the app. When restaurants raise third-party prices, repeat customers feel the squeeze, while the restaurant never gains ownership of customer phone numbers or order history.
Direct ordering via WhatsApp solves both issues. In markets across India, Southeast Asia, and Latin America, WhatsApp is already installed on virtually every smartphone. When I built the digital ordering platform for [Bread & Bite](/projects/3), a cloud kitchen in Navi Mumbai, the goal was simple: provide a fast, app-like digital menu in React that compiles the customer's cart and sends a clean, structured order message straight to the kitchen's WhatsApp Business account with zero middleman cuts.
Many software teams overcomplicate direct ordering by building native iOS/Android apps or complex server backends with real-time sockets. For standard delivery operations, this creates needless maintenance overhead and slows down loading times. Customers will not download a 60MB native mobile app just to order two burgers from a local eatery.
A lightweight, statically hosted React application running on a global CDN loads in less than 800 milliseconds on a mobile 4G connection. The entire cart state, modifier calculations, and delivery validation run client-side. Once the user enters their delivery address and taps 'Order via WhatsApp', the application executes an encoded URI string using the official WhatsApp Click-to-Chat schema (`https://wa.me/<number>?text=<encoded_text>`).
export interface CartItem {
id: string;
name: string;
variant?: string;
price: number;
quantity: number;
notes?: string;
}
export interface OrderPayload {
customerName: string;
phone: string;
address: string;
landmark?: string;
paymentMethod: 'COD' | 'UPI';
items: CartItem[];
subtotal: number;
deliveryFee: number;
grandTotal: number;
}
export function generateWhatsAppMessage(order: OrderPayload): string {
const dateStr = new Date().toLocaleString('en-IN', {
timeZone: 'Asia/Kolkata',
dateStyle: 'short',
timeStyle: 'short'
});
let text = `*NEW ORDER - BREAD & BITE*\n`;
text += `Time: ${dateStr}\n`;
text += `--------------------------------\n`;
text += `*Customer Details:*\n`;
text += `Name: ${order.customerName}\n`;
text += `Phone: ${order.phone}\n`;
text += `Address: ${order.address}${order.landmark ? ` (Near ${order.landmark})` : ''}\n`;
text += `Payment: ${order.paymentMethod}\n`;
text += `--------------------------------\n`;
text += `*Order Items:*\n`;
order.items.forEach((item, index) => {
const itemTotal = item.price * item.quantity;
text += `${index + 1}. ${item.name}${item.variant ? ` (${item.variant})` : ''} x ${item.quantity} = ₹${itemTotal}\n`;
if (item.notes) {
text += ` _Note: ${item.notes}_\n`;
}
});
text += `--------------------------------\n`;
text += `Subtotal: ₹${order.subtotal}\n`;
text += `Delivery: ₹${order.deliveryFee}\n`;
text += `*Total Amount: ₹${order.grandTotal}*\n`;
text += `--------------------------------\n`;
text += `Please reply with *CONFIRM* to begin preparation.`;
return encodeURIComponent(text);
}Opening WhatsApp from a web browser requires attention to device context. On mobile devices (iOS Safari and Android Chrome), calling `window.open` inside an asynchronous event handler can trigger popup blockers. You should generate the URI synchronously within the submit button's click boundary.
import { useState } from 'react';
import { generateWhatsAppMessage, OrderPayload } from './orderUtils';
const RESTAURANT_WHATSAPP_NUMBER = '919325629256';
export function useWhatsAppCheckout() {
const [isSubmitting, setIsSubmitting] = useState(false);
const dispatchOrder = (order: OrderPayload) => {
setIsSubmitting(true);
try {
const encodedMessage = generateWhatsAppMessage(order);
const deepLink = `https://wa.me/${RESTAURANT_WHATSAPP_NUMBER}?text=${encodedMessage}`;
// Direct location change ensures mobile OS intercepts the link directly into native WhatsApp
window.location.href = deepLink;
} catch (err) {
console.error('Failed to dispatch order:', err);
alert('Could not open WhatsApp. Please try again or call the restaurant directly.');
} finally {
setIsSubmitting(false);
}
};
return { dispatchOrder, isSubmitting };
}While small takeaway spots can manage orders directly on their phone screen, busier outlets need integration with a kitchen ticket workflow. In our production builds, we offer two paths:
This hybrid approach gives restaurants the zero-commission advantage of direct consumer relations while keeping back-of-house kitchen operations orderly and reliable.
If you manage a restaurant, cloud kitchen, or retail store and want to eliminate platform commissions with a custom web menu, explore my [full-stack web development services](/services) or [get in touch with Aafaque Nazir directly](/contact) to discuss your project requirements.
The client browser maintains the cart state locally in memory and localStorage. When the user confirms the order, the React app serializes the cart items, delivery details, and totals into an encoded text message and opens a prefilled WhatsApp chat via the wa.me deep-link protocol.
Yes. You can integrate UPI intent links, Razorpay, or Stripe payment links directly into the React checkout before generating the WhatsApp order payload, attaching the payment reference ID inside the final text summary.
Restaurants can run WhatsApp Business with quick replies, automated greeting templates, and multi-agent web access. For larger volumes, incoming orders can be synced to a central kitchen display system such as the one built for Restaurant OS / Corner Cafe.