Build a Website with Next.js
Build a 6-page dating agency site with Next.js and shadcn/ui.
Introduction
30 Second Summary
When someone searches for a dating agency online, the website is their first impression. A slow, clunky site with no clear navigation tells visitors the service behind it is just as unprofessional.
In this project, you will build a professional website for Love Agency using Next.js and shadcn/ui. You'll create 6 pages with instant navigation, a polished Home page with calls-to-action, and SEO metadata so search engines can find the site.
What You'll Build
Open localhost:3000 and you'll see a dating agency website that feels like a real product. Click between pages and watch them swap instantly with no browser reload. The Home page sells the service with a hero section, trust-building cards, and a prominent button driving visitors to sign up.
By the end of this project, you'll have:
- A Next.js project with 6 pages using file-based routing. One file per page, zero router configuration.
- A shared layout with a navigation header (including social links and a Sign Up button) and a branded footer that persists across every page.
- A professional Home page built with shadcn/ui components: a hero section, "Why Choose Us" cards, and a banner CTA driving visitors to sign up.
- Secret Mission: Build out the About page with the founder bio section and clickable social media links.
Are there any prerequisites?
This project assumes you're comfortable with React components, JSX, and Tailwind CSS basics. You don't need any prior experience with Next.js or shadcn/ui. Step 1 walks you through setting both up from scratch.
Before We Start
Before we dive in, let's lock in what you're about to build and why it matters.
You're building a professional website for Love Agency, a dating service in Cameroon. Take a moment to picture the finished site: the pages visitors will land on, the layout they'll see on every page, and what the Home page needs to communicate.
Set Up Your Next.js Project
Your dating agency website needs a working project before you can build any pages. Next.js provides server-side rendering (great for SEO) and file-based routing out of the box.
In this step, you will scaffold a Next.js project using the flags recommended by shadcn/ui, then initialize shadcn/ui and install the components you will use throughout this project.
In this step, get ready to:
- Create a Next.js project with TypeScript and Tailwind CSS.
- Initialize shadcn/ui and add the Button and Card components.
- Start the development server and confirm it runs.
Create the Next.js project
- In VS Code, press Ctrl+` (the backtick key, above Tab) to open the integrated terminal.
- Move to your Desktop by running this command:
cd ~/Desktop
- Create your Next.js project by running this command:
npx create-next-app@latest love-agency --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"
What might I see before it starts?
If this is your first time running create-next-app, npm will ask if you want to install the package. Type y and press Enter to proceed.
The command then installs all dependencies automatically. You will see progress output scrolling in your terminal for about a minute.
What do these flags do?
- --typescript enables TypeScript for type-safe code.
- --tailwind pre-configures Tailwind CSS for styling.
- --eslint adds ESLint to catch code mistakes early.
- --app uses the App Router (Next.js's modern routing system).
- --src-dir keeps your source code in a src/ folder for cleaner organization.
- --import-alias "@/*" lets you import files using short paths like @/components/header instead of long relative paths.
- Once the command finishes, move into your new project folder by running:
cd love-agency
- Confirm the project was created by running:
ls src/app
You should see files like page.tsx, layout.tsx, and globals.css listed in the output.
Don't see these files?
- Make sure the create-next-app command finished without errors. Scroll up in your terminal to check for red error messages.
- Confirm you ran cd love-agency before running ls.
- If the command failed, delete the folder with rm -rf ~/Desktop/love-agency and run the create-next-app command again.
Still stuck? Help me troubleshoot my create-next-app setup.
Initialize shadcn/ui and add components
shadcn/ui is a component library that generates ready-made UI components directly into your project. You own the code completely and can customize it however you want.
- Initialize shadcn/ui by running this command:
npx shadcn@latest init
What will the prompts look like?
This command asks several questions about your project style and configuration (color theme, CSS variables, import aliases). Press Enter for each prompt to accept the default value.
- Add the Button and Card components by running this command:
npx shadcn@latest add button card
- Confirm the components were added by running:
ls src/components/ui
You should see button.tsx and card.tsx listed in the output.
Don't see button.tsx or card.tsx?
- Make sure you ran npx shadcn@latest init first. The add command requires initialization.
- Check that you accepted the defaults during init. If something went wrong, run npx shadcn@latest init again and press Enter for each prompt.
- Confirm you are inside the love-agency folder by running pwd.
Still stuck? Help me troubleshoot my shadcn/ui setup.
Start the dev server and confirm it runs
Everything is installed. Time to see your project running in the browser.
- Before you run the next command, what do you think you will see at localhost:3000? A blank page, an error, or something else?
- Start the development server by running:
npm run dev
What does this command do?
npm run dev starts a local development server that watches your files for changes. It takes over this terminal session, so you will see log output here as the server runs.
- Open your browser and go to http://localhost:3000.
You should see the default Next.js starter page with the Next.js logo and links to documentation.
Don't see the Next.js page?
- Check your terminal for error messages. A common cause is another process already using port 3000.
- Make sure you typed http://localhost:3000 in your browser address bar, not in a search engine.
- If the terminal says the port is in use, stop the other process or restart your terminal and try npm run dev again.
Still stuck? Help me get my Next.js dev server running.
Your environment is ready. Next up, you will replace the default page with your own content and create all six pages for Love Agency.
Create Your Pages and See Them Live
Your Next.js project is running and showing the default starter page. Right now, that page has nothing to do with Love Agency.
In this step, you will replace the default page with your own content and create all the pages Love Agency needs. Next.js uses file-based routing. Creating a file at src/app/about/page.tsx automatically makes the /about URL work. No router configuration needed.
In this step, get ready to:
- Replace the default home page with your own Love Agency content.
- Create route files for all 5 additional pages.
- Visit each page in the browser to confirm file-based routing works.
Replace the default home page
The default src/app/page.tsx contains Next.js demo content with links to their documentation. You need your own component here instead.
- In VS Code, open src/app/page.tsx from the Explorer sidebar.
- Select all the existing code with Cmd+A (macOS) or Ctrl+A (Windows).
- Replace everything with the following code:
export default function HomePage() {
return (
<div>
<h1>Love Agency</h1>
<p>Find your perfect match in Cameroon</p>
</div>
);
}
What does this code do?
This exports a React component called HomePage as the default export. Next.js renders whatever component page.tsx exports at the corresponding URL.
Since this file lives directly inside src/app/, it maps to the root URL (/). This is your baby version of the home page. You will build it out properly in Step 4.
- Save src/app/page.tsx.
- Switch to your browser and look at http://localhost:3000. You should see a plain heading that says "Love Agency" and a paragraph underneath.
Still seeing the default Next.js page?
- Make sure you saved the file. Unsaved changes in VS Code show a dot on the file tab.
- Check that you replaced all the content. If any of the original imports remain at the top, delete them.
- Confirm your dev server is still running in the terminal. If it stopped, run npm run dev again.
Why is my home page not updating?
Create route files for your other pages
Love Agency needs five more pages: About, Services, Blog, Contact, and Signup. Each page needs its own folder inside src/app/ with a page.tsx file inside it.
- Create the five page folders by running this command in a new terminal (open one with Ctrl+` in VS Code):
mkdir -p src/app/about src/app/services src/app/blog src/app/contact src/app/signup
- Confirm the folders were created by running:
ls src/app
You should see about, blog, contact, services, and signup listed alongside the existing files.
Now create a page.tsx file inside each folder. Each file exports a simple component with a heading.
- Create src/app/about/page.tsx with the following code:
export default function AboutPage() {
return <h1>About Love Agency</h1>;
}
- Create src/app/services/page.tsx with the following code:
export default function ServicesPage() {
return <h1>Our Services</h1>;
}
- Save src/app/about/page.tsx and src/app/services/page.tsx.
- Switch to your browser and type http://localhost:3000/about in the address bar. You should see the heading "About Love Agency".
File-based routing in action. You created a folder and file, and the URL appeared automatically.
- Create src/app/blog/page.tsx with the following code:
export default function BlogPage() {
return <h1>Blog</h1>;
}
- Create src/app/contact/page.tsx with the following code:
export default function ContactPage() {
return <h1>Contact Us</h1>;
}
- Create src/app/signup/page.tsx with the following code:
export default function SignupPage() {
return <h1>Sign Up</h1>;
}
- Save all three files.
What is file-based routing?
In Next.js, folders inside src/app/ define your URL structure. Each folder represents one URL segment, and the page.tsx inside it is what Next.js renders at that URL.
No router configuration file needed. Creating src/app/signup/page.tsx instantly makes /signup a working URL. This is why it is called file-based routing.
Seeing a 404 page?
- Make sure the file is named exactly page.tsx (not Page.tsx or index.tsx). Next.js only recognises the lowercase page name.
- Check that the file is inside a folder matching the route name (e.g. src/app/blog/page.tsx for /blog).
- Confirm the component uses export default. Without the default export, Next.js cannot find the page component.
Why is my route returning 404?
Visit every page and spot the gap
All six pages are in place. Before you move on, take a moment to predict what will happen when you try to move between pages.
- Think about this: if you are on the About page and you want to get to the Services page, how would you do it right now?
- Type http://localhost:3000/services in your address bar to visit the Services page.
- Type http://localhost:3000/blog to visit the Blog page.
- Type http://localhost:3000/contact to visit the Contact page.
- Type http://localhost:3000/signup to visit the Signup page.
Each page shows its heading. But notice: there is no way to navigate between pages without manually typing URLs. No header, no links, no menu. That is the problem Step 3 solves.
✔️ Awesome, I've got everything!
Great. Double check you have saved all six files.
ⓧ I'd like to double check the full code
Here is what each file should contain. Compare yours against these:
export default function HomePage() {
return (
<div>
<h1>Love Agency</h1>
<p>Find your perfect match in Cameroon</p>
</div>
);
}
export default function AboutPage() {
return <h1>About Love Agency</h1>;
}
export default function ServicesPage() {
return <h1>Our Services</h1>;
}
export default function BlogPage() {
return <h1>Blog</h1>;
}
export default function ContactPage() {
return <h1>Contact Us</h1>;
}
export default function SignupPage() {
return <h1>Sign Up</h1>;
}
Your six pages are live and every URL works. Next up, you will build a shared layout with a navigation header so visitors can actually move between pages without typing URLs.
Build the Shared Layout with Navigation
Your six pages are live, but they are isolated islands. There is no way for a visitor to move between them without manually typing a URL in the address bar.
In this step, you will build a shared Header and Footer that appear on every page. Along the way, you will discover why regular HTML links are not enough for a modern React app and how Next.js solves the problem with its Link component.
In this step, get ready to:
- Create a Header with navigation links, social media links, and a Sign Up button.
- Create a Footer with quick links, social links, and a Sign Up button.
- Wire both into a shared layout so every page gets them automatically.
Create the Header component with navigation links
The Header needs navigation links to every main page. You will start by using regular HTML <a> tags and see what happens when you click between pages.
- Create a new file at src/components/header.tsx and add the following code:
"use client";
const navLinks = [
{ href: "/", label: "Home" },
{ href: "/about", label: "About" },
{ href: "/services", label: "Services" },
{ href: "/blog", label: "Blog" },
{ href: "/contact", label: "Contact" },
{ href: "/signup", label: "Sign Up" },
];
export function Header() {
return (
<header className="w-full">
<nav className="bg-white border-b px-4 py-4">
<div className="max-w-6xl mx-auto flex items-center justify-between">
<a href="/" className="text-2xl font-bold text-pink-600">
Love Agency
</a>
<div className="flex items-center gap-6">
{navLinks.map((link) => (
<a key={link.href} href={link.href} className="text-gray-700 hover:text-pink-600">
{link.label}
</a>
))}
</div>
</div>
</nav>
</header>
);
}
What does this code do?
This is a simplified Header component using standard HTML <a> tags for every link. The "use client" directive marks this as a client component, which you will need shortly for interactive features.
The navLinks array keeps your link data in one place so you can map over it instead of repeating HTML.
The Header exists as a file, but nothing renders it yet. You need to add it to your root layout so it appears on every page.
- Open src/app/layout.tsx in VS Code.
- Add this import near the top of the file, below the existing imports:
import { Header } from "@/components/header";
- Inside the <body> tag, add <Header /> directly above {children}.
- Save both files.
Before you click any links, what do you think will happen when you navigate from Home to About using these regular <a> tags?
- In your browser at localhost:3000, you should now see the Header with navigation links at the top of the page.
- Click the About link in the Header.
- Watch the browser tab closely as the page loads.
You will see the browser tab show a loading spinner. The page flashes white, the scroll position resets, and the entire page reloads from scratch. Click a few more links and watch it happen every time.
Why does the page reload?
Regular <a> tags tell the browser to fetch a completely new HTML page from the server. Every click throws away the current page (including any JavaScript state) and starts fresh.
This is the default behaviour of the web. It works, but it feels slow and clunky compared to apps like Gmail or Twitter where content changes instantly. Next.js solves this with its Link component, which intercepts clicks and swaps only the content that changed.
Now you will upgrade this Header to the full professional version. The new version adds a social media bar, uses the Next.js Link component for instant navigation, includes a shadcn Button for Sign Up, and has a mobile menu toggle.
- Delete everything in src/components/header.tsx and replace it with this code at the top of the file:
"use client";
import { useState } from "react";
import Link from "next/link";
import { Button } from "@/components/ui/button";
const navLinks = [
{ href: "/", label: "Home" },
{ href: "/about", label: "About" },
{ href: "/services", label: "Services" },
{ href: "/blog", label: "Blog" },
{ href: "/contact", label: "Contact" },
];
const socialLinks = [
{ href: "https://facebook.com", label: "Facebook" },
{ href: "https://instagram.com", label: "Instagram" },
{ href: "https://tiktok.com", label: "TikTok" },
{ href: "https://t.me", label: "Telegram" },
];
What changed in the imports?
You now import Link from next/link. This component looks like an <a> tag but intercepts clicks and navigates without a full page reload.
The useState import powers the mobile menu toggle. The Button import is the shadcn component you installed earlier.
- Below the data arrays, add the Header function with the social bar:
export function Header() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
return (
<header className="w-full">
<div className="bg-gray-900 text-white py-2 px-4">
<div className="max-w-6xl mx-auto flex justify-end gap-4 text-sm">
{socialLinks.map((link) => (
<a
key={link.label}
href={link.href}
target="_blank"
rel="noopener noreferrer"
className="hover:text-pink-400 transition-colors"
>
{link.label}
</a>
))}
</div>
</div>
Why are social links still regular <a> tags?
The social links (Facebook, Instagram, TikTok, Telegram) point to external websites. Next.js Link is only for navigating within your own app. External links use regular <a> tags with target="_blank" so they open in a new tab.
- Continuing in the same file, add the main navigation bar below the social bar div:
<nav className="bg-white border-b px-4 py-4">
<div className="max-w-6xl mx-auto flex items-center justify-between">
<Link href="/" className="text-2xl font-bold text-pink-600">
Love Agency
</Link>
<div className="hidden md:flex items-center gap-6">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className="text-gray-700 hover:text-pink-600 transition-colors"
>
{link.label}
</Link>
))}
<Button asChild>
<Link href="/signup">Sign Up</Link>
</Button>
</div>
<button
className="md:hidden text-gray-700"
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
>
{mobileMenuOpen ? "Close" : "Menu"}
</button>
</div>
What is the asChild prop?
The asChild prop on Button tells shadcn to render the child element (in this case a Link) instead of a <button> tag. This gives you the styling of a Button with the navigation behaviour of a Link.
- Finally, add the mobile menu and close the component:
{mobileMenuOpen && (
<div className="md:hidden mt-4 flex flex-col gap-3 px-4">
{navLinks.map((link) => (
<Link
key={link.href}
href={link.href}
className="text-gray-700 hover:text-pink-600 transition-colors"
onClick={() => setMobileMenuOpen(false)}
>
{link.label}
</Link>
))}
<Button asChild className="w-full">
<Link href="/signup">Sign Up</Link>
</Button>
</div>
)}
</nav>
</header>
);
}
- Save src/components/header.tsx.
- Switch to your browser and click the About link in the Header.
The page content changes instantly. No loading spinner, no white flash, no scroll reset. The dark social bar at the top now shows Facebook, Instagram, TikTok, and Telegram links, and a pink Sign Up button sits in the navigation.
Still seeing a full page reload?
Make sure every internal navigation link uses <Link href="..."> from next/link, not a regular <a> tag. Check that the import import Link from "next/link" is at the top of the file.
If you see a compile error, check for missing closing tags or mismatched braces. Help me fix my Header component
Create the Footer component
The Footer gives visitors another way to navigate and reinforces the brand. Since you have already learned the difference between Link and <a>, you will use the correct one from the start.
- Create a new file at src/components/footer.tsx and add the imports and data arrays:
import Link from "next/link";
import { Button } from "@/components/ui/button";
const quickLinks = [
{ href: "/", label: "Home" },
{ href: "/about", label: "About" },
{ href: "/services", label: "Services" },
{ href: "/blog", label: "Blog" },
{ href: "/contact", label: "Contact" },
];
const socialLinks = [
{ href: "https://facebook.com", label: "Facebook" },
{ href: "https://instagram.com", label: "Instagram" },
{ href: "https://tiktok.com", label: "TikTok" },
{ href: "https://t.me", label: "Telegram" },
];
- Below the data arrays, add the Footer function with the company description and quick links:
export function Footer() {
return (
<footer className="bg-gray-900 text-white py-12 px-4">
<div className="max-w-6xl mx-auto grid grid-cols-1 md:grid-cols-4 gap-8">
<div>
<h3 className="text-xl font-bold text-pink-400 mb-3">Love Agency</h3>
<p className="text-gray-400 text-sm">
Professional dating agency connecting hearts in Cameroon. We arrange
personalized meetings for serious individuals looking for love.
</p>
</div>
<div>
<h4 className="font-semibold mb-3">Quick Links</h4>
<ul className="space-y-2">
{quickLinks.map((link) => (
<li key={link.href}>
<Link
href={link.href}
className="text-gray-400 hover:text-pink-400 text-sm transition-colors"
>
{link.label}
</Link>
</li>
))}
</ul>
</div>
- Continue in the same file, adding the social links section and the Get Started column:
<div>
<h4 className="font-semibold mb-3">Follow Us</h4>
<ul className="space-y-2">
{socialLinks.map((link) => (
<li key={link.label}>
<a
href={link.href}
target="_blank"
rel="noopener noreferrer"
className="text-gray-400 hover:text-pink-400 text-sm transition-colors"
>
{link.label}
</a>
</li>
))}
</ul>
</div>
- Finally, add the Get Started column, the copyright bar, and close the component:
<div>
<h4 className="font-semibold mb-3">Get Started</h4>
<p className="text-gray-400 text-sm mb-4">
Ready to find your perfect match? Join Love Agency today.
</p>
<Button asChild>
<Link href="/signup">Sign Up</Link>
</Button>
</div>
</div>
<div className="max-w-6xl mx-auto mt-8 pt-8 border-t border-gray-700 text-center text-gray-500 text-sm">
© {new Date().getFullYear()} Love Agency. All rights reserved.
</div>
</footer>
);
}
What does this Footer include?
The Footer has four columns at desktop width: a brand description, quick links (using Link for internal pages), social media links (using regular <a> tags with target="_blank"), and a Get Started section with another Sign Up button.
The copyright line uses new Date().getFullYear() so it always shows the current year without manual updates.
- Save src/components/footer.tsx.
Wire both components into the root layout
Next.js uses a special file called layout.tsx to wrap every page with shared UI. Anything you place in this file appears on all pages automatically.
- Open src/app/layout.tsx and replace its entire contents with this:
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import { Header } from "@/components/header";
import { Footer } from "@/components/footer";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "Love Agency",
description: "Professional dating agency connecting hearts in Cameroon",
};
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body className={inter.className}>
<Header />
<main className="min-h-screen">{children}</main>
<Footer />
</body>
</html>
);
}
What does this layout do?
The RootLayout function wraps every page in your app. The {children} placeholder is replaced by whichever page the visitor is on.
By placing <Header /> above {children} and <Footer /> below it, both components appear on every single page without repeating any code. The metadata export sets the default page title and description for SEO.
- Save src/app/layout.tsx.
- Switch to your browser at localhost:3000.
What do you expect to see at the top and bottom of the page now?
- Click through all the navigation links: Home, About, Services, Blog, Contact.
- Click the Sign Up button in the Header.
- Scroll down and click the Sign Up button in the Footer.
Every page shows the Header and Footer. Clicking any navigation link swaps the page content instantly with no browser reload. Both Sign Up buttons navigate to the /signup page.
Footer not showing up?
Check that your layout.tsx has import { Footer } from "@/components/footer" at the top and <Footer /> below the </main> tag.
If you see a compile error in your terminal, look for unmatched JSX tags in footer.tsx. Make sure every opening <div> has a closing </div>. Help me fix my layout or footer
✔️ Awesome, I've got everything!
Great. Double check you have saved all three files: header.tsx, footer.tsx, and layout.tsx.
ⓧ I'd like to double check the full code
Scroll up and confirm you have all the chunks in order for each file:
- header.tsx: imports and data arrays at the top, then the Header function with the social bar, then the nav section with desktop links and mobile button, then the mobile menu and closing tags.
- footer.tsx: imports and data arrays, then the Footer function with company description and quick links, then the social links section, then the Get Started column with copyright and closing tags.
- layout.tsx: the full replacement shown in the final substep.
Your site now has professional navigation that works like a real web app. Next up, you will build out the Home page with a hero section, feature cards, and a call-to-action banner using shadcn/ui components.
Build the Home Page with shadcn/ui
Your navigation and layout are working perfectly. Every page shares a header and footer, and clicking between them is instant.
The Home page is your agency's storefront. Right now it is just a heading. It needs to communicate what Love Agency does, why prospects should choose you, and drive them toward signing up. This is where shadcn/ui components make the page look professional without writing CSS from scratch.
In this step, get ready to:
- Build a hero section with a headline and call-to-action button.
- Add a "Why Choose Us" section using shadcn Card components.
- Add a banner CTA and SEO metadata to all pages.
Build the hero section
The hero section is the first thing a visitor sees. It needs a big headline, a short explanation of the service, and a button that drives visitors to sign up.
- Open src/app/page.tsx in VS Code.
- Select all the existing code and delete it.
- Replace it with these imports and SEO metadata by pasting this code:
import type { Metadata } from "next";
import Link from "next/link";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardHeader,
CardTitle,
} from "@/components/ui/card";
export const metadata: Metadata = {
title: "Love Agency - Find Your Perfect Match",
description:
"Professional dating agency in Cameroon. We arrange personalized meetings for serious individuals looking for love.",
};
What does this code do?
The Metadata export tells search engines what your page is about. When someone searches for "dating agency Cameroon," Google uses your title and description to decide whether to show your page.
The imports pull in the shadcn Button and Card components you installed earlier. You also import Link from Next.js for client-side navigation.
- Now add the hero section below the metadata by pasting this code at the bottom of the file:
export default function HomePage() {
return (
<div>
<section className="py-20 px-4 text-center bg-gradient-to-b from-pink-50 to-white">
<div className="max-w-3xl mx-auto">
<h1 className="text-4xl md:text-5xl font-bold text-gray-900 mb-6">
Find Your Perfect Match
</h1>
<p className="text-lg text-gray-600 mb-8">
Love Agency is Cameroon's premier dating service. We arrange
personalized meetings for serious individuals looking for a genuine
connection.
</p>
<Button asChild size="lg">
<Link href="/signup">Get Started</Link>
</Button>
</div>
</section>
</div>
);
}
What does this code do?
The asChild prop on Button tells shadcn to render the child element (a Link) instead of a regular <button> tag. This gives you button styling with client-side navigation built in.
The Tailwind classes handle all the spacing, gradient background, and responsive text sizing. No custom CSS file needed.
- Save src/app/page.tsx.
- Switch to your browser and check localhost:3000.
You should see a large "Find Your Perfect Match" headline centered on the page with a pink-to-white gradient background and a "Get Started" button below it.
Don't see the hero section?
- Make sure you deleted ALL the old code before pasting. If any of the old export default function HomePage remains, you will get a duplicate export error.
- Check that the import lines are at the very top of the file with no blank lines above them.
- If you see a red error overlay mentioning Card imports, that is fine for now. The Card imports are used in the next chunk.
Still stuck? Help me debug my hero section in page.tsx
Add the "Why Choose Us" section
Below the hero, you need to show visitors why Love Agency is different. Three cards will highlight the agency's key strengths using shadcn Card components.
- In src/app/page.tsx, find the closing }; of the metadata export.
- Add this data array between the metadata and the export default function line:
const reasons = [
{
title: "Personalized Matching",
description:
"Our experts take the time to understand your values, preferences, and goals to find your ideal partner.",
},
{
title: "Professional Interview",
description:
"Every candidate goes through a personal interview to ensure serious intentions and compatibility.",
},
{
title: "Guaranteed Discretion",
description:
"Your personal information and journey with us remain completely confidential at every step.",
},
];
What does this code do?
This array stores the three reasons as data objects. By mapping over this array in JSX, you avoid copy-pasting the same Card markup three times. If you add a fourth reason later, you just add another object to the array.
- Now find the closing </section> tag of the hero section (the one right before </div>).
- Add this "Why Choose Us" section directly below that closing </section> tag:
<section className="py-16 px-4">
<div className="max-w-5xl mx-auto">
<h2 className="text-3xl font-bold text-center mb-12">
Why Choose Us
</h2>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{reasons.map((reason) => (
<Card key={reason.title} className="text-center">
<CardHeader>
<CardTitle className="text-lg">{reason.title}</CardTitle>
</CardHeader>
<CardContent>
<p className="text-gray-600 text-sm">{reason.description}</p>
</CardContent>
</Card>
))}
</div>
</div>
</section>
What does this code do?
The reasons.map() call loops through your data array and renders one Card per reason. Each Card uses CardHeader and CardContent sub-components to structure the title and description.
The Tailwind grid classes make the cards stack on mobile (1 column) and sit side-by-side on larger screens (3 columns).
- Save src/app/page.tsx and check your browser.
You should see three cards in a row below the hero: "Personalized Matching", "Professional Interview", and "Guaranteed Discretion".
Cards not appearing?
- Check that the reasons array is placed OUTSIDE the function, between the metadata export and export default function HomePage().
- Verify the new <section> is inside the outer <div> but after the hero </section> closing tag.
- If you see an error about Card not being defined, check that your import at the top of the file includes Card, CardContent, CardHeader, CardTitle.
Need more help? Help me fix my Why Choose Us cards section
Add the banner CTA and finalize your pages
The bottom of the Home page needs a bold, colored banner that gives visitors one final push to sign up. You will also add SEO metadata to your remaining pages so search engines can index them.
- In src/app/page.tsx, find the closing </section> tag of the "Why Choose Us" section.
- Add this banner CTA section directly below it (still inside the outer <div>):
<section className="py-20 px-4 text-center bg-pink-600 text-white">
<div className="max-w-2xl mx-auto">
<h2 className="text-3xl font-bold mb-4">Ready to Find Love?</h2>
<p className="text-pink-100 mb-8">
Join Love Agency today and let us help you find your perfect match.
Your journey to lasting love starts here.
</p>
<Button asChild size="lg" variant="secondary">
<Link href="/signup">Sign Up Now</Link>
</Button>
</div>
</section>
What does this code do?
The variant="secondary" prop swaps the button to a lighter color so it stands out against the pink background. The asChild prop works the same as before, wrapping a Link for instant navigation to /signup.
- Save src/app/page.tsx and check your browser.
You should see a bold pink section at the bottom with "Ready to Find Love?" and a "Sign Up Now" button. Click it to confirm it navigates to /signup.
Banner not showing pink background?
- Make sure the class is bg-pink-600 (not bg-pink-400 or another shade).
- Confirm the section is inside the outer <div> wrapper, not outside it. If it is outside the component's return, nothing renders.
Still stuck? Help me debug my banner CTA section
✔️ Awesome, I've got everything!
Great. Make sure you have saved src/app/page.tsx before moving on.
ⓧ I'd like to double check the full code
Scroll up and confirm your file contains these sections in this order:
- Imports (Metadata, Link, Button, Card components) at the top.
- The metadata export with title and description.
- The reasons array with 3 objects (Personalized Matching, Professional Interview, Guaranteed Discretion).
- The HomePage function returning a <div> with three <section> elements: hero, Why Choose Us cards, and the pink banner CTA.
Now you will add SEO metadata to your remaining pages. This is the same pattern you used on the Home page, but simpler since these pages only need a title, description, and placeholder content.
- Open src/app/about/page.tsx and replace everything with this code:
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "About - Love Agency",
description: "Learn about Love Agency and our mission to connect hearts in Cameroon.",
};
export default function AboutPage() {
return (
<div className="max-w-4xl mx-auto py-16 px-4">
<h1 className="text-3xl font-bold mb-4">About Love Agency</h1>
<p className="text-gray-600">This page will be built out in the Secret Mission.</p>
</div>
);
}
- Open src/app/services/page.tsx and replace everything with this code:
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Services - Love Agency",
description: "Explore our dating and matchmaking services at Love Agency.",
};
export default function ServicesPage() {
return (
<div className="max-w-4xl mx-auto py-16 px-4">
<h1 className="text-3xl font-bold mb-4">Our Services</h1>
<p className="text-gray-600">Services details coming soon.</p>
</div>
);
}
- Save both files.
- Visit localhost:3000/about in your browser. Check the browser tab title. It should say "About - Love Agency".
- Open src/app/blog/page.tsx and replace everything with this code:
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Blog - Love Agency",
description: "Read our latest articles about dating, relationships, and love.",
};
export default function BlogPage() {
return (
<div className="max-w-4xl mx-auto py-16 px-4">
<h1 className="text-3xl font-bold mb-4">Blog</h1>
<p className="text-gray-600">Articles coming soon.</p>
</div>
);
}
- Open src/app/contact/page.tsx and replace everything with this code:
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Contact - Love Agency",
description: "Get in touch with Love Agency or request a rendez-vous.",
};
export default function ContactPage() {
return (
<div className="max-w-4xl mx-auto py-16 px-4">
<h1 className="text-3xl font-bold mb-4">Contact Us</h1>
<p className="text-gray-600">Contact form coming in a future project.</p>
</div>
);
}
- Save both files and visit localhost:3000/blog. Check the browser tab. It should say "Blog - Love Agency".
- Open src/app/signup/page.tsx and replace everything with this code:
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Sign Up - Love Agency",
description: "Register with Love Agency to find your perfect match.",
};
export default function SignupPage() {
return (
<div className="max-w-4xl mx-auto py-16 px-4">
<h1 className="text-3xl font-bold mb-4">Sign Up</h1>
<p className="text-gray-600">Registration form coming in a future project.</p>
</div>
);
}
- Save the file.
Why add metadata to placeholder pages?
Search engines crawl every page on your site, not just the Home page. Even placeholder pages benefit from a unique title and description. When you build these pages out later, the SEO foundation is already in place.
Before you run the final check, what do you expect to see when you click the "Get Started" button on the Home page?
- Visit localhost:3000 in your browser.
- Confirm you see the hero section with "Find Your Perfect Match", three cards below it, and the pink "Ready to Find Love?" banner at the bottom.
- Click the "Get Started" button in the hero. It should navigate instantly to /signup.
- Click the "Sign Up Now" button in the pink banner. It should also navigate to /signup.
Both buttons navigate you to the Sign Up page with no page reload. The browser tab on the signup page shows "Sign Up - Love Agency".
Your Home page is ready to impress. In the Secret Mission, you will build out the About page with a founder bio section and clickable social media links.
Secret mission
Build the About Page
The About page builds trust with potential clients. Can you build out the founder bio section with a photo placeholder, clickable social media links, and the company story using the shadcn/ui patterns you learned?
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely.
This project runs entirely locally, so there are no ongoing costs.
Resources you used:
- The love-agency project folder on your Desktop (contains all source code, dependencies, and configuration).
- The Next.js development server process (running in your terminal).
Keep everything running
No action needed. Choose this if you are still actively building or plan to continue with Project 2 (connecting to your Express backend and MongoDB).
- Your code stays in the love-agency folder on your Desktop.
- Run npm run dev from inside that folder anytime to restart the dev server.
- If you closed the terminal, open a new one, navigate to ~/Desktop/love-agency, and run npm run dev again.
Pause - I'll come back to this later
Stop the dev server to free up your terminal and system resources. Your files stay intact for when you return.
- Switch to the terminal where your dev server is running.
- Press Ctrl+C to stop the server.
- Your project files remain in ~/Desktop/love-agency. Run npm run dev from that folder whenever you want to pick back up.
Delete - I don't want to use this again
Remove all project files and start fresh. This deletes everything you built in this project.
- Switch to the terminal where your dev server is running and press Ctrl+C to stop it.
- Delete the project folder by running the command for your operating system below.
macOS
rm -rf ~/Desktop/love-agency
Windows
rmdir /s /q %USERPROFILE%\Desktop\love-agency
- Confirm the folder is gone by checking your Desktop. The love-agency folder should no longer appear.
That's a Wrap!
That's a Wrap!
Nice work! You just built a fully routed, professionally styled website for Love Agency using Next.js and shadcn/ui.
You've learned how to:
- Built a shared layout with a Header and Footer that persists across all pages, including client-side navigation using Next.js Link for instant page transitions with no browser reload.
- Used file-based routing to create 6 pages with zero router configuration. One file per page, one URL per folder.
- Built a professional Home page with a hero section, "Why Choose Us" cards, and a banner CTA using shadcn/ui components and added SEO metadata so search engines can index your site.
- Secret Mission: Built the About page with a founder bio section, a photo placeholder, clickable social media links, and a company story card.
Ready to quiz yourself?