We revamped our site to better serve our users!
Frontend Hire
Login Register Flow

Building Landing Page

In this lesson, we will build a landing page for our application.

The landing page will contain a header, main content, and a footer. The header will contain a logo and a link to the login page. The main content will contain a welcome message and the footer will contain the copyright information. We will also learn about route groups and layouts in Next.js. A sample About Us page will also be created to demonstrate the use of layouts.

Landing Page

We will create a route group that will hold the landing page. This route group will contain a single route that will render the landing page component.

Why Route Groups?

Route groups are useful when you want to group related routes. In our case, we want to group all the routes related to the landing page. This helps with layering our application and keeping the codebase organized.

For example, all the marketing page routes will be grouped under the (marketing) route group, this could be in real-world applications including pages like the home page, about page, contact page, etc. Whereas the auth page routes could be grouped under the (auth) route group and hold pages like login, register, forgot password, etc. This is what we will do in this course.

Let's create a landing page that will contain a link to the login page and we will put it under the (marketing) route group.

import Link from 'next/link';
 
export default function Home() {
  return (
    <div className="flex h-svh flex-col">
      <header className="bg-gray-300">
        <div className="container mx-auto flex justify-between p-4">
          <span>Logo</span>
          <Link href="/login">Login</Link>
        </div>
      </header>
      <main className="grow">
        <section className="container mx-auto p-4">
          <h1 className="text-3xl font-bold">Welcome to our website</h1>
          <p className="text-gray-700">This is the landing page</p>
        </section>
      </main>
      <footer className="container mx-auto p-4 text-center text-sm">
        © Frotend Hire {new Date().getFullYear()}
      </footer>
    </div>
  );
}

In the code above, we created a simple landing page component that contains a header, main content, and footer. The header contains a logo and a link to the login page. The main content contains a welcome message and the footer contains the copyright information.

Now, let us say, we want to add another page for About Us to the landing page group, we can easily do that by adding a new file under the (marketing) directory. Let us create a new file called about/page.tsx under the (marketing) directory. Next.js follows the file-based routing system, so the folder name will be the route path and the file name for the page will always be page.tsx in the App router.

export default function About() {
  return (
    <div className="container mx-auto p-4">
      <h1 className="text-3xl font-bold">About Us</h1>
      <p className="text-gray-700">This is the about us page</p>
    </div>
  );
}

Let us also link the About Us page in the header of the landing page.

import Link from 'next/link';
 
export default function Home() {
  return (
    <div className="flex h-svh flex-col">
      <header className="bg-gray-300">
        <div className="container mx-auto flex justify-between p-4">
          <span>Logo</span>
          <nav>
            <ul className="flex gap-4">
              <li>
                <Link href="/about-us">About Us</Link>
              </li>
              <li>
                <Link href="/login">Login</Link>
              </li>
            </ul>
          </nav>
        </div>
      </header>
      <main className="grow">
        <section className="container mx-auto p-4">
          <h1 className="text-3xl font-bold">Welcome to our website</h1>
          <p className="text-gray-700">This is the landing page</p>
        </section>
      </main>
      <footer className="container mx-auto p-4 text-center text-sm">
        © Frotend Hire {new Date().getFullYear()}
      </footer>
    </div>
  );
}

Clicking on the About Us link will take you to the About Us page. But wait, our header and footer do not show up on the About Us page. This is because we are not reusing the header and footer components. Let us refactor the header and footer into separate components and reuse them in the landing page and the About Us page.

We also assume that this header and footer will only be used in the (marketing) route group and not in other route groups. So, we will create a new directory called src/app/(marketing)/_components and move the header and footer components into this directory. If we want to use them in other route groups, storing them in a src/app/components directory would be a better choice.

Why the _ (an underscore) prefix in the _components directory? Though App router allows for safe colocation by default, there is still a risk that someone creates a page.tsx inside the components directory and it gets picked up by the router. To avoid this, we use the _ (an underscore) prefix to indicate that this directory should not be picked up by the router and is a private directory.

import Link from 'next/link';
 
export default function Header() {
  return (
    <header className="bg-gray-300">
      <div className="container mx-auto flex justify-between p-4">
        <span>Logo</span>
        <nav>
          <ul className="flex gap-4">
            <li>
              <Link href="/about-us">About Us</Link>
            </li>
            <li>
              <Link href="/login">Login</Link>
            </li>
          </ul>
        </nav>
      </div>
    </header>
  );
}
export default function Footer() {
  return (
    <footer className="container mx-auto p-4 text-center text-sm">
      © Frotend Hire {new Date().getFullYear()}
    </footer>
  );
}

Now, let us update the landing page and the About Us page to use the header and footer components.

import Header from './_components/header';
import Footer from './_components/footer';
 
export default function Home() {
  return (
    <div className="flex h-svh flex-col">
      <Header />
      <main className="grow">
        <section className="container mx-auto p-4">
          <h1 className="text-3xl font-bold">Welcome to our website</h1>
          <p className="text-gray-700">This is the landing page</p>
        </section>
      </main>
      <Footer />
    </div>
  );
}
import Header from '../_components/header';
import Footer from '../_components/footer';
 
export default function About() {
  return (
    <div className="flex h-svh flex-col">
      <Header />
      <main className="grow">
        <section className="container mx-auto p-4">
          <h1 className="text-3xl font-bold">About Us</h1>
          <p className="text-gray-700">This is the about us page</p>
        </section>
      </main>
      <Footer />
    </div>
  );
}

Layout for our Route Group

We can see that the header and footer are common across all the pages in the (marketing) route group. We can create a layout component that will contain the header and footer and wrap the page content with this layout component. This will help us avoid repeating the header and footer in every page component in the (marketing) route group.

Let us create a layout file called layout.tsx under the (marketing) directory.

import Footer from './_components/footer';
import Header from './_components/header';
 
export default function Layout({ children }: React.PropsWithChildren) {
  return (
    <div className="flex h-svh flex-col">
      <Header />
      <main className="grow">{children}</main>
      <Footer />
    </div>
  );
}

Now, we should see a duplication of the header and footer on the landing page and the About Us page. Let us refactor these pages to remove the header and footer components.

export default function Home() {
  return (
    <section className="container mx-auto p-4">
      <h1 className="text-3xl font-bold">Welcome to our website</h1>
      <p className="text-gray-700">This is the landing page</p>
    </section>
  );
}

The about page.

export default function About() {
  return (
    <section className="container mx-auto p-4">
      <h1 className="text-3xl font-bold">About Us</h1>
      <p className="text-gray-700">This is the about us page</p>
    </section>
  );
}

Perfect, you can see that the header and footer are now being reused across the landing page and the About Us page. This is the power of layouts in Next.js and is also common in other frontend frameworks and routing libraries.

Bonus: Tailwind CSS Container Class Power Up

Did you notice a couple of Tailwind CSS classes we repeated in the header, main, and footer components? It is the container mx-auto p-4 classes. These classes are used to center the content and add padding to the content. This is so common that we can configure Tailwind CSS to add these classes by default to the container class. This is done by updating the tailwind.config.ts file.

tailwind.config.ts
import type { Config } from 'tailwindcss';
 
const config: Config = {
  content: [
    './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
    './src/components/**/*.{js,ts,jsx,tsx,mdx}',
    './src/app/**/*.{js,ts,jsx,tsx,mdx}',
  ],
  theme: {
    extend: {
      container: {
        center: true,
        padding: '16px',
      },
      backgroundImage: {
        'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
        'gradient-conic':
          'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))',
      },
    },
  },
  plugins: [],
};
export default config;

You can read more about the container class here. Though we did add padding to the container class, it only adds horizontal padding. For vertical padding, we would still have to add a py-4 class to the container. This makes sense because the vertical padding might vary based on the design requirements. Also, sometimes headers are built using height instead of padding for spacing. So, it is better to keep the vertical padding separate.

Let us refactor the header, the footer, and the pages to remove the container mx-auto p-4 classes.

import Link from 'next/link';
 
export default function Header() {
  return (
    <header className="bg-gray-300">
      <div className="container flex justify-between py-4">
        <span>Logo</span>
        <nav>
          <ul className="flex gap-4">
            <li>
              <Link href="/about-us">About Us</Link>
            </li>
            <li>
              <Link href="/login">Login</Link>
            </li>
          </ul>
        </nav>
      </div>
    </header>
  );
}

The footer.

export default function Footer() {
  return (
    <footer className="container py-2 text-center text-sm">
      © Frotend Hire {new Date().getFullYear()}
    </footer>
  );
}

The landing page.

export default function Home() {
  return (
    <section className="container py-2">
      <h1 className="text-3xl font-bold">Welcome to our website</h1>
      <p className="text-gray-700">This is the landing page</p>
    </section>
  );
}

The about page.

export default function About() {
  return (
    <section className="container py-2">
      <h1 className="text-3xl font-bold">About Us</h1>
      <p className="text-gray-700">This is the about us page</p>
    </section>
  );
}

That is it for this chapter, we are ready to build our login page now.

At this point, our code should match the code in the branch 2-building-landing-page.

On this page