W'Foood | Restaurant Landing Page 1 - Next.js, JavaScript, TailwindCSS, Framer Motion Frontend Project
A modern, responsive restaurant website built with Next.js 14, featuring smooth animations, interactive maps, and a beautiful UI. This project demonstrates best practices for building static websites with React Server Components, TailwindCSS, and modern animation libraries.
- Live-Demo: https://restaurant-wfood.vercel.app/
- Project Overview
- Features
- Technology Stack
- Project Structure
- Installation
- Running the Project
- Environment Variables
- Project Walkthrough
- Component Documentation
- Routes and Navigation
- Reusing Components
- Code Examples
- Customization Guide
- Deployment
- Learning Resources
- Keywords
- Conclusion
W'Food is a modern restaurant website showcasing local flavors and cuisine. Built as a static site using Next.js 14 App Router, it features:
- Responsive Design: Mobile-first approach with breakpoints for all screen sizes
- Smooth Animations: Framer Motion animations for engaging user experience
- Interactive Maps: React-Leaflet integration for location display
- Modern UI Components: Radix UI components for accessible form elements
- SEO Optimized: Comprehensive metadata and Open Graph tags
- Performance Optimized: Image optimization, font optimization, and code splitting
This project serves as both a production-ready website and an educational resource for learning modern React/Next.js development patterns.
- Hero Section: Eye-catching introduction with animated text and images
- Menu Display: Showcase featured dishes with hover effects and responsive grid
- Reservation System: Interactive form with date picker and party size selector
- About Section: Two-column layout with restaurant information
- Interactive Map: Multiple location markers with popups
- Responsive Navigation: Desktop and mobile navigation with smooth scrolling
- Footer: Links, social media, and copyright information
- Server-Side Rendering (SSR): Fast initial page loads
- Static Site Generation (SSG): Pre-rendered pages for optimal performance
- Dynamic Imports: Code splitting for better performance
- Font Optimization: Self-hosted Google Fonts via Next.js
- Image Optimization: Automatic image optimization with Next.js Image component
- Accessibility: ARIA labels, keyboard navigation, and semantic HTML
- SEO: Comprehensive metadata, Open Graph, and Twitter Card tags
- Next.js 14.2.35: React framework with App Router, SSR, and SSG
- React 18: UI library with hooks and modern patterns
- JavaScript (ES6+): Modern JavaScript features
- TailwindCSS 3.3: Utility-first CSS framework
- tailwindcss-animate: Animation utilities for Tailwind
- Custom CSS Variables: Font and color management
- Framer Motion 10.16: Production-ready animation library
- react-scroll: Smooth scrolling navigation
- react-responsive: Media query hooks
- Radix UI: Accessible component primitives
@radix-ui/react-popover: Popover component@radix-ui/react-select: Select dropdown@radix-ui/react-label: Form labels@radix-ui/react-slot: Polymorphic components
- Lucide React: Modern icon library
- React Icons: Comprehensive icon collection
- React-Leaflet 4.2: React wrapper for Leaflet maps
- Leaflet 1.9: Interactive maps library
- date-fns 2.30: Date utility library
- react-day-picker 8.9: Date picker component
- class-variance-authority: Type-safe variant system
- clsx: Conditional class names
- tailwind-merge: Merge Tailwind classes intelligently
- ESLint: Code linting
- PostCSS: CSS processing
- Autoprefixer: CSS vendor prefixing
restaurant-js/
βββ app/ # Next.js App Router directory
β βββ layout.js # Root layout with metadata and fonts
β βββ page.js # Homepage component
β βββ globals.css # Global styles and Tailwind directives
β βββ favicon.ico # Site favicon
βββ components/ # React components
β βββ About.jsx # About section component
β βββ Footer.jsx # Footer component
β βββ Header.jsx # Header with navigation
β βββ Hero.jsx # Hero section component
β βββ Map.jsx # Interactive map component
β βββ Menu.jsx # Menu display component
β βββ Nav.jsx # Desktop navigation
β βββ NavMobile.jsx # Mobile navigation
β βββ Reservation.jsx # Reservation section wrapper
β βββ ReservationForm.jsx # Reservation form component
β βββ StyleGuide.jsx # Style guide component
β βββ ui/ # Reusable UI components
β βββ button.jsx # Button component with variants
β βββ calendar.jsx # Calendar component
β βββ input.jsx # Input component
β βββ label.jsx # Label component
β βββ popover.jsx # Popover component
β βββ select.jsx # Select dropdown component
βββ lib/ # Utility functions
β βββ utils.js # Class name merging utility
βββ public/ # Static assets
β βββ about/ # About section images
β βββ footer/ # Footer background
β βββ hero/ # Hero section images
β βββ map/ # Map marker images
β βββ menu/ # Menu item images
β βββ reservation/ # Reservation background
β βββ logo.svg # Site logo
β βββ pin-solid.svg # Map pin icon
βββ variants.js # Framer Motion animation variants
βββ components.json # shadcn/ui configuration
βββ jsconfig.json # JavaScript path aliases
βββ next.config.js # Next.js configuration
βββ tailwind.config.js # TailwindCSS configuration
βββ postcss.config.js # PostCSS configuration
βββ package.json # Dependencies and scripts
βββ README.md # Project documentationBefore you begin, ensure you have the following installed:
- Node.js 18.x or higher (Download)
- npm 9.x or higher (comes with Node.js)
- Git (for cloning the repository)
git clone https://github.com/arnobt78/RestaurantJS--TailwindCSS-Fundamental-Project-5.git
cd RestaurantJS--TailwindCSS-Fundamental-Project-5Using npm:
npm installOr using yarn:
yarn installOr using pnpm:
pnpm installOr using bun:
bun installCheck that all dependencies are installed correctly:
npm list --depth=0Start the development server with hot reloading:
npm run devThe application will be available at http://localhost:3000
Features in Development Mode:
- Hot Module Replacement (HMR)
- Fast Refresh for React components
- Error overlay in the browser
- Source maps for debugging
Create an optimized production build:
npm run buildThis command:
- Compiles and optimizes all code
- Generates static pages
- Minifies JavaScript and CSS
- Optimizes images
- Creates production-ready files in
.nextdirectory
After building, start the production server:
npm startThe production server will be available at http://localhost:3000
Check code quality and style:
npm run lintThis project is a static website that doesn't require:
- API keys
- Database connections
- Server-side environment variables
- Third-party service credentials
All data is static and embedded in components. The map uses public tile layers that don't require authentication.
If you extend this project with:
-
Backend API Integration
NEXT_PUBLIC_API_URL=https://api.example.com NEXT_PUBLIC_API_KEY=your-api-key
-
Analytics Services
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX NEXT_PUBLIC_HOTJAR_ID=your-hotjar-id
-
Map Service API Keys
NEXT_PUBLIC_MAPBOX_TOKEN=your-mapbox-token NEXT_PUBLIC_GOOGLE_MAPS_KEY=your-google-maps-key
-
Content Management System
NEXT_PUBLIC_CMS_URL=https://cms.example.com NEXT_PUBLIC_CMS_TOKEN=your-cms-token
If you need environment variables:
-
Create
.env.localin the root directory:touch .env.local
-
Add your variables:
# Example environment variables NEXT_PUBLIC_API_URL=http://localhost:3000/api NEXT_PUBLIC_SITE_URL=http://localhost:3000
-
Access in code:
// In any component or page const apiUrl = process.env.NEXT_PUBLIC_API_URL;
Important Notes:
- Variables prefixed with
NEXT_PUBLIC_are exposed to the browser - Variables without prefix are server-only
- Never commit
.env.localto version control - Add
.env.localto.gitignore
This project uses Next.js 14 App Router, which provides:
- File-based Routing: Files in
app/directory become routes - Server Components: Default components are server-rendered
- Client Components: Marked with
"use client"for interactivity - Layouts: Shared UI across routes
- Metadata API: SEO and social sharing configuration
User visits / (homepage)
β
app/page.js renders
β
Components load in order:
1. Header (fixed navigation)
2. Hero (main banner)
3. Menu (featured dishes)
4. Reservation (booking form)
5. About (restaurant info)
6. Map (location)
7. Footer (links and copyright)
RootLayout (app/layout.js)
βββ Home (app/page.js)
βββ Header
β βββ Nav (desktop)
β βββ NavMobile (mobile)
βββ Hero
βββ Menu
βββ Reservation
β βββ ReservationForm
β βββ Input (ui)
β βββ Calendar (ui)
β βββ Select (ui)
βββ About
βββ Map
βββ Footer
Root layout component that wraps all pages.
Features:
- Font optimization (Lora, Poppins)
- SEO metadata configuration
- Open Graph and Twitter Card tags
- Global styles application
Key Code:
export const metadata = {
title: {
default: "W'Food - A Taste of Local Flavours",
template: "%s | W'Food Restaurant",
},
description: "Experience authentic local flavours...",
// ... more metadata
};
export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={`${lora.variable} ${poppins.variable}`}>{children}</body>
</html>
);
}Homepage component that composes all sections.
Features:
- Dynamic imports for performance
- Component composition
- Static page generation
Key Code:
// Dynamic import prevents SSR issues with Leaflet
const MyMap = dynamic(() => import("../components/Map"), { ssr: false });
export default function Home() {
return (
<main className="w-full max-w-[1440px] bg-white mx-auto overflow-hidden">
<Header />
<Hero />
<Menu />
<Reservation />
<About />
<MyMap />
<Footer />
</main>
);
}Fixed header with scroll detection and responsive navigation.
Features:
- Scroll-based background change
- Desktop and mobile navigation
- Smooth scroll to sections
- Event listener cleanup
Key Code:
const [active, setActive] = useState(false);
useEffect(() => {
const handleScroll = () => {
setActive(window.scrollY > 100);
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);Props: None (self-contained)
Usage:
<Header />Desktop navigation component with smooth scrolling.
Features:
- Smooth scroll to sections
- Active link highlighting
- Configurable styles via props
Props:
containerStyles: CSS classes for nav containerlinkStyles: CSS classes for links
Usage:
<Nav
containerStyles="hidden xl:flex gap-x-12 text-white"
linkStyles="capitalize"
/>Mobile hamburger menu with slide-in animation.
Features:
- Toggleable sidebar menu
- Icon-based navigation
- Smooth animations
- Click outside to close
Props:
containerStyles: CSS classes for containericonStyles: CSS classes for iconslinkStyles: CSS classes for links
Usage:
<NavMobile
containerStyles="xl:hidden"
iconStyles="text-3xl"
linkStyles="uppercase"
/>Hero section with animated text and images.
Features:
- Framer Motion animations
- Responsive image display
- Call-to-action button
- Staggered animations
Key Code:
<motion.h1
variants={fadeIn("down", 0.2)}
initial="hidden"
whileInView={"show"}
viewport={{ once: false, amount: 0.4 }}
>
A taste of local flavours
</motion.h1>Usage:
<Hero />Menu items display with hover effects.
Features:
- Responsive grid layout
- Image hover animations
- Price display
- Link to full menu
Data Structure:
const menu = [
{
img: "/menu/item-1.png",
title: "Stilton and pancetta penne",
price: "$24.00",
},
// ... more items
];Usage:
<Menu />Customization:
Replace the menu array with your own data:
const menu = [
{
img: "/menu/your-item.png",
title: "Your Dish Name",
price: "$XX.XX",
},
];About section with two-column layout.
Features:
- Text and image layout
- Framer Motion animations
- Responsive design
- Read more button
Usage:
<About />Reservation section with interactive form.
Features:
- Date picker (react-day-picker)
- Party size selector
- Form inputs
- Background image
Form Fields:
- First name
- Last name
- Date selection
- Number of people
Usage:
<Reservation />Note: Currently UI-only. To add functionality:
const handleSubmit = (e) => {
e.preventDefault();
// Add your form submission logic
// e.g., API call, email service, etc.
};Interactive map with multiple markers.
Features:
- React-Leaflet integration
- Custom marker icons
- Popup information
- Responsive zoom levels
Configuration:
const markers = [
{
position: [34.052235, -118.243683], // [lat, lng]
title: "Location 1",
subtitle: "Description",
image: "/map/1.png",
},
];Usage:
<Map />Customization:
Update markers array with your locations:
const markers = [
{
position: [YOUR_LATITUDE, YOUR_LONGITUDE],
title: "Your Location",
subtitle: "Your description",
image: "/map/your-image.png",
},
];Footer with links and social media.
Features:
- Three-column layout
- Blog links
- New items section
- Social media links
- Copyright information
Usage:
<Footer />Reusable button component with variants.
Variants:
default: Green primary buttonorange: Orange accent buttoninput: Transparent input-style button
Sizes:
default: 170px Γ 62pxsm: 150px Γ 58px
Usage:
<Button variant="orange" size="sm">
Click me
</Button>With asChild (polymorphic):
<Button asChild variant="default">
<Link href="/menu">View Menu</Link>
</Button>Styled input component.
Usage:
<Input id="email" type="email" placeholder="Enter email" />Accessible select dropdown.
Usage:
<Select>
<SelectTrigger>
<SelectValue placeholder="Select option" />
</SelectTrigger>
<SelectContent>
<SelectItem value="option1">Option 1</SelectItem>
<SelectItem value="option2">Option 2</SelectItem>
</SelectContent>
</Select>Date picker calendar component.
Usage:
<Calendar mode="single" selected={date} onSelect={setDate} initialFocus />Framer Motion animation variants.
Function:
export const fadeIn = (direction, delay) => {
return {
hidden: {
y: direction === "up" ? 80 : direction === "down" ? -80 : 0,
opacity: 0,
// ... more config
},
show: {
y: 0,
x: 0,
opacity: 1,
// ... more config
},
};
};Usage:
import { fadeIn } from "@/variants";
<motion.div variants={fadeIn("up", 0.2)}>Content</motion.div>;Class name merging utility.
Function:
export function cn(...inputs) {
return twMerge(clsx(inputs));
}Usage:
import { cn } from "@/lib/utils";
<div className={cn("base-class", condition && "conditional-class")} />;Next.js App Router uses file-based routing:
app/
βββ page.js β / (homepage)
βββ about/
β βββ page.js β /about
βββ menu/
β βββ page.js β /menu
βββ contact/
βββ page.js β /contact
This project currently has one route:
/(Homepage) -app/page.js
- Create a new directory in
app/:
mkdir app/about- Create
page.js:
export default function AboutPage() {
return <div>About Page</div>;
}- Add navigation link in
components/Nav.jsx:
const links = [
// ... existing links
{
path: "about",
name: "about",
offset: -50,
},
];The project uses react-scroll for smooth section navigation:
import { Link as ScrollLink } from "react-scroll";
<ScrollLink to="menu" smooth={true} offset={-50}>
Menu
</ScrollLink>;Parameters:
to: Section ID to scroll tosmooth: Enable smooth scrollingoffset: Pixel offset from top
All components are self-contained and can be easily reused.
Copy the component file to your project:
cp components/Hero.jsx /path/to/your/project/components/Ensure required dependencies are installed:
npm install framer-motion next/imageCopy utility files if needed:
cp variants.js /path/to/your/project/
cp lib/utils.js /path/to/your/project/lib/Update import paths to match your project structure:
// Before
import { fadeIn } from "@/variants";
// After (if using different alias)
import { fadeIn } from "../variants";Ensure Tailwind classes match your configuration, or update them:
// Update Tailwind classes to match your theme
className = "bg-hero"; // Make sure 'hero' is defined in tailwind.config.js// Change text content
<motion.h1>
Your Custom Heading
</motion.h1>
// Change images
<Image
src="/your-hero-image.png"
width={756}
height={682}
alt="your alt text"
/>
// Change animation
variants={fadeIn("left", 0.5)} // Different direction and delay// Replace menu data
const menu = [
{
img: "/your-menu/item-1.png",
title: "Your Dish",
price: "$XX.XX",
},
];
// Change grid columns
className = "grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3";// Update map center
center={[YOUR_LAT, YOUR_LNG]}
// Update markers
const markers = [
{
position: [YOUR_LAT, YOUR_LNG],
title: "Your Location",
subtitle: "Your description",
image: "/map/your-image.png"
},
];
// Change map style
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png""use client";
import { fadeIn } from "@/variants";
import { motion } from "framer-motion";
export default function NewSection() {
return (
<motion.section
variants={fadeIn("up", 0.2)}
initial="hidden"
whileInView={"show"}
viewport={{ once: false, amount: 0.2 }}
className="py-20"
>
<div className="container mx-auto">
<motion.h2
variants={fadeIn("down", 0.3)}
initial="hidden"
whileInView={"show"}
>
Your Section Title
</motion.h2>
</div>
</motion.section>
);
}// In tailwind.config.js
buttonVariants: {
// ... existing variants
outline: "border-2 border-green text-green hover:bg-green hover:text-white",
}
// Usage
<Button variant="outline">Click me</Button>import { useState } from "react";
const ReservationForm = () => {
const [formData, setFormData] = useState({
firstName: "",
lastName: "",
date: null,
people: "",
});
const [errors, setErrors] = useState({});
const validate = () => {
const newErrors = {};
if (!formData.firstName) newErrors.firstName = "Required";
if (!formData.lastName) newErrors.lastName = "Required";
if (!formData.date) newErrors.date = "Please select a date";
if (!formData.people) newErrors.people = "Please select party size";
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleSubmit = (e) => {
e.preventDefault();
if (validate()) {
// Submit form
console.log("Form submitted:", formData);
}
};
return <form onSubmit={handleSubmit}>{/* Form fields */}</form>;
};// app/api/reservation/route.js
export async function POST(request) {
const data = await request.json();
// Process reservation
// Save to database, send email, etc.
return Response.json({ success: true });
}
// In component
const handleSubmit = async (e) => {
e.preventDefault();
const response = await fetch("/api/reservation", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(formData),
});
const result = await response.json();
// Handle result
};Edit tailwind.config.js:
colors: {
green: { DEFAULT: '#YOUR_COLOR', hover: '#YOUR_HOVER_COLOR' },
orange: { DEFAULT: '#YOUR_COLOR', hover: '#YOUR_HOVER_COLOR' },
// ... more colors
}- Update
app/layout.js:
import { YourFont } from "next/font/google";
const yourFont = YourFont({
subsets: ["latin"],
variable: "--font-your-font",
});- Update
tailwind.config.js:
fontFamily: {
yourFont: ['var(--font-your-font)', 'sans-serif'],
}Edit tailwind.config.js:
screens: {
sm: '640px',
md: '768px',
lg: '1024px', // Changed from 960px
xl: '1280px', // Changed from 1200px
}- Create component:
// components/Testimonials.jsx
export default function Testimonials() {
return <section id="testimonials">...</section>;
}- Add to homepage:
// app/page.js
import Testimonials from "../components/Testimonials";
export default function Home() {
return (
<main>
{/* ... existing components */}
<Testimonials />
</main>
);
}- Add to navigation:
// components/Nav.jsx
const links = [
// ... existing links
{
path: "testimonials",
name: "testimonials",
offset: -50,
},
];- Push code to GitHub
- Import project in Vercel
- Configure build settings:
- Framework Preset: Next.js
- Build Command:
npm run build - Output Directory:
.next
- Deploy
Vercel automatically:
- Detects Next.js
- Optimizes builds
- Provides CDN
- Handles environment variables
- Push code to GitHub
- Import project in Netlify
- Configure:
- Build command:
npm run build - Publish directory:
.next
- Build command:
- Deploy
AWS Amplify:
- Connect repository
- Auto-detects Next.js
- Configure build settings
DigitalOcean App Platform:
- Connect GitHub repository
- Select Node.js environment
- Configure build and run commands
Self-Hosting:
npm run build
npm startFramework & Libraries:
- Next.js, React, JavaScript, ES6+, App Router, Server Components
Styling:
- TailwindCSS, CSS, Responsive Design, Mobile-First, Utility-First CSS
Animation:
- Framer Motion, Animations, Transitions, Scroll Animations
UI/UX:
- Modern UI, User Interface, User Experience, Interactive Design
Features:
- Restaurant Website, Menu Display, Reservation System, Interactive Map, Smooth Scrolling
Development:
- Static Site Generation, Server-Side Rendering, Code Splitting, Performance Optimization
Tools:
- Vercel, Deployment, Git, npm, Package Management
Learning:
- Tutorial, Educational, Learning Project, Code Examples, Best Practices
This project demonstrates modern web development practices using Next.js 14, React, and TailwindCSS. It showcases:
- Component-Based Architecture: Reusable, maintainable components
- Performance Optimization: Image optimization, code splitting, font optimization
- Modern Animations: Smooth, engaging user experiences
- Responsive Design: Mobile-first approach with breakpoints
- SEO Best Practices: Comprehensive metadata and Open Graph tags
- Accessibility: Semantic HTML and ARIA labels
- Developer Experience: Clean code, educational comments, best practices
By exploring this project, you've gained experience with:
- Next.js App Router and file-based routing
- React Server Components and Client Components
- TailwindCSS utility-first styling
- Framer Motion animations
- React-Leaflet map integration
- Form handling with Radix UI
- Responsive design patterns
- Performance optimization techniques
- SEO and metadata configuration
- Component reusability and composition
- Extend Functionality: Add backend API, database integration
- Add More Pages: Create About, Menu, Contact pages
- Enhance Animations: Experiment with different animation patterns
- Add Testing: Implement unit and integration tests
- Optimize Further: Add analytics, improve performance metrics
- Customize Design: Create your own color scheme and branding
Feel free to:
- Fork the repository
- Create feature branches
- Submit pull requests
- Report issues
- Suggest improvements
This project is licensed under the MIT License. Feel free to use, modify, and distribute the code as per the terms of the license.
This is an open-source project - feel free to use, enhance, and extend this project further!
If you have any questions or want to share your work, reach out via GitHub or my portfolio at https://www.arnobmahmud.com.
Enjoy building and learning! π
Thank you! π





