Restaurant Page React template for Tailwind CSS v4
v1.0.0Added in 1.14.0
A React restaurant website template: live opening hours, a tabbed menu with dietary filters, table booking, a gallery, events and a map.
A restaurant's home page. The full-bleed hero shows whether the restaurant is open right now in its own time zone (Open until 11 PM, Closing soon, Closed now, opens tomorrow at 10 AM) and updates every minute; Reserve a table glides down to the booking form and focuses it, Order online links out. The menu has category tabs (arrow keys move between them) and dietary filter toggles (Vegan, Vegetarian, Gluten free, Spicy) in one bar that sticks under the header while you scroll the list; on phones both are swipeable chip rows. Filters combine, the tabs then show how many dishes match, and an empty section offers Clear filters. Every dish shows its price (glass and bottle for wines), dietary tags and a highlight badge. The reservation form takes the party size (steppers, typing, arrow keys), a date from the calendar (closed weekdays and past days are disabled, a bottom sheet on phones) and a time from chips per service (Brunch, Dinner) every timeStep minutes, with booked times struck through and past times hidden; the next bookable day is preselected. Name and email are validated on submit and focus moves to the first problem; the button spins while onReserve runs, then a confirmation shows the date, time, guests and a confirmation number with Add to calendar (an .ics file) and Make another booking. The gallery opens a full-screen lightbox (swipe, arrows, pinch zoom). Private event cards are one link each. The visit section has the address, Get directions, call, the week's hours grouped by day with today marked, and a map that opens full screen on touch. On phones a Reserve a table bar sticks to the bottom while the form is off screen.
Restaurant Page is a Pro React template in the React website templates category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add restaurant-page, together with Marketing Section, Marketing Header, Marketing Footer, Testimonials Section, CTA Section and 15 more and the npm packages lucide-react and motion.
Built with
The sections this page is made of. In the full-screen preview, hold Alt to outline each one on the page.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add restaurant-pageRestaurant Page is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import RestaurantPage from @/components/templates/restaurant-page.
// app/page.tsx
"use client"
import Link from "next/link"
import { RestaurantPage, type RestaurantReservation } from "@/components/templates/restaurant-page"
async function reserve(reservation: RestaurantReservation) {
const response = await fetch("/api/reservations", { method: "POST", body: JSON.stringify(reservation) })
if (!response.ok) throw new Error("Booking failed")
return (await response.json()) as { confirmation: string }
}
export default function HomeRoute() {
return <RestaurantPage linkComponent={Link} timeZone="America/Chicago" partySizeMax={8} onReserve={reserve} />
}Props
designdefault"warm""warm" | "dark" | "minimal"The whole page's look. warm: a soft amber page with tinted bands and raised cards; dark: a night page in both themes (the tokens switch to dark under the root, overlays render inside it); minimal: a plain page with no bands, an inset hero and border-only cards. Each design picks its own backgrounds unless you set them.
restaurantdefault"RESTAURANT_INFO"RestaurantInfo{ name, cuisine, priceRange?, street, city, neighborhood?, phone, email, point: { lat, lng }, rating?, reviewCount?, directionsNote? }: the brand, hero line, contact block and map pin.
hoursdefault"RESTAURANT_HOURS"RestaurantDayHours[]{ day: 0 Sunday ... 6 Saturday, ranges: { label?, open: "17:00", close: "23:00" }[] } per weekday; no ranges is closed, a close at or before the open runs past midnight. Drives the open status, the hours table, the closed days of the calendar and the time slots.
categoriesdefault"RESTAURANT_MENU_CATEGORIES"RestaurantMenuCategory[]{ id, label, description? }: the menu tabs, in order.
menudefault"RESTAURANT_MENU"RestaurantMenuItem[]{ id, category, name, description?, price, variants?: { label, price }[], tags?: string[], highlight?, soldOut? }: the dishes and drinks. variants (Glass, Bottle) replace the price; tags are dietary tag ids.
dietaryTagsdefault"RESTAURANT_DIETARY_TAGS"RestaurantDietaryTag[]{ id, label, icon?, tone? }: the dietary marks and filters. vegan, vegetarian, gluten-free and spicy get a drawn icon by id; an empty array hides the filters.
storydefault"RESTAURANT_STORY"RestaurantStory{ eyebrow, title, paragraphs, chefName, chefRole, stats: { value, label }[], scene?, image? }: the chef story beside a picture.
gallerydefault"RESTAURANT_GALLERY"RestaurantGalleryImage[]{ src?, scene?, alt, caption? }: the gallery tiles; without src the scene is drawn (pasta, pizza, salad, steak, fish, dessert, cocktail, wine, room, oven, cellar, terrace).
eventSpacesdefault"RESTAURANT_EVENT_SPACES"RestaurantEventSpace[]{ id, name, description, capacity, minimum?, scene?, image?, href? }: the private event cards; href defaults to eventsHref#id.
reviewsdefault"RESTAURANT_REVIEWS"Testimonial[]Guest quotes with names, the occasion as role and star ratings, shown by TestimonialsSection.
heroImagedefault""stringA photo url for the hero; without it the dining room with the oven is drawn in code.
orderHrefdefault"/order"stringThe hero's Order online button; empty hides it.
eventsHrefdefault"/private-events"stringPlan an event and the event cards link here.
giftCardsHrefdefault"/gift-cards"stringThe gift card band's main button.
directionsHrefdefault""stringGet directions; defaults to a Google Maps search for the address.
labelsPartial<RestaurantPageLabels>Every string on the page (DEFAULT_RESTAURANT_PAGE_LABELS), including the open status sentences, validation messages and the confirmation.
partySizeMindefault1numberThe smallest party the form books.
partySizeMaxdefault10numberThe largest party the form books; larger groups are pointed to private events.
defaultPartySizedefault2numberThe starting party size, kept inside the range.
timeStepdefault30numberMinutes between bookable times (at least 5).
lastSeatingdefault60numberMinutes before a service closes when the last table can be booked.
minNoticedefault30numberMinutes from now before the first time that can still be booked today.
bookingWindowdefault60numberHow many days ahead the calendar opens.
timeInputdefault"slots""slots" | "picker"slots: a grid of time chips per service with booked times struck through; picker: a TimePicker field limited to the open times.
isBooked(date: string, time: string) => booleanMarks taken times ("2026-11-06", "19:30") as booked: struck through and not selectable.
onReserve(reservation: RestaurantReservation) => void | RestaurantReservationResult | Promise<RestaurantReservationResult | void | unknown>Called with { date, time, partySize, name, email, phone, notes } once the form is valid. A returned promise keeps the button loading; resolve with { confirmation } to show your number, reject to keep the form and show labels.reserveFailed. Without it, a 900 ms pretend booking.
timeZonedefault"America/Chicago"stringThe restaurant's IANA time zone: the open status, today's row, today's past times and the first bookable day follow it, wherever the guest is.
nowDateA fixed clock for screenshots and tests; default the live time, read after hydration (the server renders labels.hoursFallback).
currencydefault"USD"stringISO 4217 code for the menu prices.
localedefault""stringIntl locale for prices, dates and times; empty uses the UIProvider's, else en-US.
tonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"Accents: highlight badges, the active menu tab, pressed filters, the chosen time, the calendar selection and review stars.
ctaTonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"Reserve a table buttons, the form's submit, the sticky bar and the gift card band.
radiusdefault"lg""none" | "sm" | "md" | "lg"Corners of panels and cards (0 / 12 / 16 / 24px), with matching inner tiles and time chips.
menuColumnsdefault21 | 2Dish columns from 768px; phones always get one.
backgrounddefault"soft""plain" | "soft" | "glow" | "aurora" | "grid" | "dots"The main background. A design picks its own unless you set one (warm: soft, dark: glow, minimal: plain).
backgroundColordefault"oklch(0.72 0.11 58)"stringThe tint and glow color of the main background; each design has its own (warm amber, ember, sand).
bandBackgrounddefault"tinted""muted" | "tinted" | "gradient" | "dots"The look of the alternating bands (warm: tinted, dark: muted; minimal has no bands). Cards and fields inside a band are lifted above it.
bandColordefault"oklch(0.72 0.11 58)"stringThe tint of tinted and gradient bands; defaults to backgroundColor.
showMenudefaulttruebooleanThe menu with its tabs and filters.
showReservationdefaulttruebooleanThe booking form. Off, the Reserve buttons and the sticky bar go too.
showStorydefaulttruebooleanThe chef story with its picture and numbers.
showGallerydefaulttruebooleanThe photo grid and its lightbox.
showEventsdefaulttruebooleanThe private event spaces.
showReviewsdefaulttruebooleanThe guest reviews.
showLocationdefaulttruebooleanThe address, hours table and map.
showCtadefaulttruebooleanThe gift card band before the footer.
stickyReserveBardefaulttruebooleanOn phones, a Reserve a table bar with the open status sticks to the bottom while the form is off screen.
headerdefaulttruebooleanThe sticky header with the brand, section links, call and Reserve.
footerdefaulttruebooleanThe footer with Visit, About and Help links.
headerPropsPartial<MarketingHeaderProps>Anything for MarketingHeader (layout, variant, items...).
footerPropsPartial<MarketingFooterProps>Anything for MarketingFooter (variant, columns, socials...).
tabsPropsPartial<TabsRootProps>Anything for the menu TabsRoot (variant, size, activationMode...).
datePickerPropsPartial<DatePickerProps>Anything for the reservation DatePicker (disabled days, presets, calendarProps...).
timePickerPropsPartial<TimePickerProps>Anything for the TimePicker when timeInput is "picker".
galleryPropsPartial<LightboxGalleryProps>Anything for the LightboxGallery (columns, aspect, maxVisible, featured...).
reviewsPropsPartial<TestimonialsSectionProps>Anything for TestimonialsSection (layout, columns, card...).
mapPropsPartial<LocationMapProps>Anything for the LocationMap (tileUrl, height, staticOnly, interactive...).
ctaPropsPartial<CtaSectionProps>Anything for the gift card CtaSection (variant, title, hrefs...).
stickyBarPropsPartial<StickyCtaBarProps>Anything for the phone StickyCtaBar (variant, showOn, hideOnScroll...).
animateIndefaulttruebooleanThe hero copy rises in once when the page mounts on the client; never during hydration, under reduced motion or in thumbnails.
linkComponent(props: LinkComponentProps) => ReactNodeThe router link for the header, event cards and footer links (next/link in an app).
classNamestringClasses for the root.
classNamesPartial<Record<RestaurantPageSlot, string>>Classes for header, hero, heroMedia, status, menu, menuBar, filters, menuPanel, menuItem, reservation, reservationForm, slots, confirmation, story, gallery, events, eventCard, reviews, location, hours, map, cta, footer and stickyBar.
Parts
RESTAURANT_PAGE_SAMPLE
constantAll the sample content of Olive & Ember in one object: restaurant, hours, categories, dietary tags, menu, story, gallery, event spaces and reviews.
RESTAURANT_INFO
constantOlive & Ember, a wood-fired Mediterranean restaurant on Fulton Market, Chicago.
RESTAURANT_HOURS
constantClosed Mondays, dinner Tuesday to Sunday, brunch on weekends, until 11 PM on Fridays and Saturdays.
RESTAURANT_MENU_CATEGORIES
constantStarters, Mains, Desserts and Drinks.
RESTAURANT_DIETARY_TAGS
constantVegan, Vegetarian, Gluten free and Spicy.
RESTAURANT_MENU
constant22 dishes and drinks with prices, tags, highlights, glass and bottle prices and one sold out.
RESTAURANT_STORY
constantChef Sofia Marino's story with three numbers.
RESTAURANT_GALLERY
constantNine drawn scenes: the room, pasta, pizza, a negroni, salad, ribeye, branzino, tiramisu and wine.
RESTAURANT_EVENT_SPACES
constantThe Ember Room, The Cellar and a full buyout.
RESTAURANT_REVIEWS
constantSix guest reviews.
RESTAURANT_PAGE_DESIGNS
constantThe design names: ["warm", "dark", "minimal"].
DEFAULT_RESTAURANT_PAGE_LABELS
constantThe English strings.
drawRestaurantScene
function(kind) returns an svg data url of a drawn scene (a plate from above, a glass, the room, the oven), for an img or a lightbox image.
drawRestaurantHero
function() returns the wide drawn hero picture (the dining room with the oven) as an svg data url.
RestaurantPageProps
typeProps of RestaurantPage, including the attributes of the root element.
RestaurantPageLabels
typeEvery string of the page.
RestaurantPageSlot
typeThe keys of classNames.
RestaurantPageDesign
type"warm" | "dark" | "minimal".
RestaurantPageRadius
type"none" | "sm" | "md" | "lg".
RestaurantPageTimeInput
type"slots" | "picker".
RestaurantPageMenuColumns
type1 | 2.
RestaurantReservation
typeWhat onReserve receives: { date: yyyy-MM-dd, time: HH:mm, partySize, name, email, phone, notes }.
RestaurantReservationResult
typeWhat onReserve may resolve with: { confirmation? }.
RestaurantInfo
typeThe restaurant's name, cuisine, address, contact, map point and rating.
RestaurantDayHours
type{ day, ranges }: one weekday's services.
RestaurantHoursRange
type{ label?, open, close }: one service, "HH:mm" 24h.
RestaurantMenuCategory
type{ id, label, description? }: one menu tab.
RestaurantMenuItem
typeOne dish or drink.
RestaurantMenuVariant
type{ label, price }: a glass or bottle price.
RestaurantDietaryTag
type{ id, label, icon?, tone? }: a dietary mark and filter.
RestaurantStory
typeThe chef story.
RestaurantChefStat
type{ value, label }: one number of the story.
RestaurantGalleryImage
type{ src?, scene?, alt, caption? }: one gallery tile.
RestaurantEventSpace
typeOne private event card.
RestaurantSceneKind
typeThe drawn scenes: pasta, pizza, salad, steak, fish, dessert, cocktail, wine, room, oven, cellar, terrace.
Examples
Dark design
A night page in both themes: the tokens switch to dark under the root, with an ember glow and muted bands.
Minimal design
A plain page with an inset hero, no bands and border-only cards.
Brand accents
ctaTone and tone set to brand: orange Reserve buttons, highlight badges, chosen time and calendar day.
A time picker and 15 minute slots
timeInput="picker" with timeStep 15 and parties up to 6: the time field only offers open, free times.
When booking fails
onReserve rejects: the button spins, the form stays filled and says the table could not be booked.
Dependencies
Files
- components/templates/restaurant-page.tsx
- components/templates/restaurant-page-data.ts
FAQ
Is the React Restaurant Page template free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install Restaurant Page?
Run npx wingo-ui@latest add restaurant-page. It also adds the Wingo UI items it builds on (Marketing Section, Marketing Header, Marketing Footer, Testimonials Section, CTA Section and 15 more) and the npm packages lucide-react and motion. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/restaurant-page.
How does Restaurant Page behave on phones?
The menu has category tabs (arrow keys move between them) and dietary filter toggles (Vegan, Vegetarian, Gluten free, Spicy) in one bar that sticks under the header while you scroll the list; on phones both are swipeable chip rows.
See also
Related guides
- Nextjs Restaurant Template: Open Now, Menu, Book a Table
Build it: screens and apps10 min read