Getting Started with Next.js App Router Capabilities Getting Started with Next.js App Router Capabilities
Welcome back to our series on building a SaaS application using Next.js, Prisma, and Supabase! In this post, we'll delve into the new Next.js app router capabilities. We'll explore how to set up the app structure, configure routes, and manage navigation effectively. By the end of this post, you'll have a solid understanding of how to leverage Next.js routing features to build a scalable and maintainable application.
What is the Next.js App Router?
The Next.js app router simplifies the process of defining and managing routes in your application. It uses a file-based routing system, which means that the file and directory structure of your project directly determines the routes of your application. This approach makes it easy to organize and navigate your app's pages and components.
Setting Up the App Structure
Let's start by organizing our project directory to take full advantage of Next.js's app router. Here's a basic structure for our SaaS application:
my-saas-app/
βββ pages/
β βββ api/
β βββ index.tsx
β βββ dashboard.tsx
β βββ auth/
β βββ login.tsx
β βββ register.tsx
βββ components/
β βββ Layout.tsx
β βββ Navbar.tsx
β βββ Footer.tsx
βββ styles/
β βββ globals.css
βββ supabaseClient.ts
Configuring Routes and Navigation
Creating Pages
In Next.js, each file inside the pages
directory becomes a route in your application. Let's create some basic pages for our SaaS app.
Home Page (pages/index.tsx
)
1 // pages/index.tsx
2 import { NextPage } from 'next'
3 import Head from 'next/head'
4 import Link from 'next/link'
5
6 const Home : NextPage = ( ) => {
7 return (
8 < div className = "min-h-screen flex flex-col items-center justify-center bg-gray-100" >
9 < Head >
10 < title > Home | My SaaS App < / title >
11 < / Head >
12 < main className = "flex flex-col items-center justify-center w-full flex-1 px-20 text-center" >
13 < h1 className = "text-6xl font-bold" >
14 Welcome to < a className = "text-blue-600" href = "/" > My SaaS App < / a >
15 < / h1 >
16 < p className = "mt-3 text-2xl" >
17 Get started by navigating to { ' ' }
18 < Link href = "/dashboard" >
19 < a className = "text-blue-600" > Dashboard < / a >
20 < / Link >
21 < / p >
22 < / main >
23 < / div >
24 )
25 }
26
27 export default Home
28
Dashboard Page (pages/dashboard.tsx
)
1 // pages/dashboard.tsx
2 import { NextPage } from 'next'
3 import Head from 'next/head'
4 import Link from 'next/link'
5
6 const Dashboard : NextPage = ( ) => {
7 return (
8 < div className = "min-h-screen flex flex-col items-center justify-center bg-gray-100" >
9 < Head >
10 < title > Dashboard | My SaaS App < / title >
11 < / Head >
12 < main className = "flex flex-col items-center justify-center w-full flex-1 px-20 text-center" >
13 < h1 className = "text-6xl font-bold" > Dashboard < / h1 >
14 < p className = "mt-3 text-2xl" >
15 Welcome to your dashboard . Go back to { ' ' }
16 < Link href = "/" >
17 < a className = "text-blue-600" > Home < / a >
18 < / Link >
19 < / p >
20 < / main >
21 < / div >
22 )
23 }
24
25 export default Dashboard
26
Login Page (pages/auth/login.tsx
)
1 // pages/auth/login.tsx
2 import { NextPage } from 'next'
3 import Head from 'next/head'
4 import Link from 'next/link'
5
6 const Login : NextPage = ( ) => {
7 return (
8 < div className = "min-h-screen flex flex-col items-center justify-center bg-gray-100" >
9 < Head >
10 < title > Login | My SaaS App < / title >
11 < / Head >
12 < main className = "flex flex-col items-center justify-center w-full flex-1 px-20 text-center" >
13 < h1 className = "text-6xl font-bold" > Login < / h1 >
14 < form className = "mt-8 space-y-6" >
15 < div className = "rounded-md shadow-sm -space-y-px" >
16 < div >
17 < label htmlFor = "email" className = "sr-only" > Email address < / label >
18 < input id = "email" name = "email" type = "email" autoComplete = "email" required className = "appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm" placeholder = "Email address" / >
19 < / div >
20 < div >
21 < label htmlFor = "password" className = "sr-only" > Password < / label >
22 < input id = "password" name = "password" type = "password" autoComplete = "current-password" required className = "appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm" placeholder = "Password" / >
23 < / div >
24 < / div >
25 < div >
26 < button type = "submit" className = "group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" >
27 Sign in
28 < / button >
29 < / div >
30 < / form >
31 < p className = "mt-2 text-sm text-gray-600" >
32 Or { ' ' }
33 < Link href = "/auth/register" >
34 < a className = "font-medium text-indigo-600 hover:text-indigo-500" > create a new account < / a >
35 < / Link >
36 < / p >
37 < / main >
38 < / div >
39 )
40 }
41
42 export default Login
43
Register Page (pages/auth/register.tsx
)
1 // pages/auth/register.tsx
2 import { NextPage } from 'next'
3 import Head from 'next/head'
4 import Link from 'next/link'
5
6 const Register : NextPage = ( ) => {
7 return (
8 < div className = "min-h-screen flex flex-col items-center justify-center bg-gray-100" >
9 < Head >
10 < title > Register | My SaaS App < / title >
11 < / Head >
12 < main className = "flex flex-col items-center justify-center w-full flex-1 px-20 text-center" >
13 < h1 className = "text-6xl font-bold" > Register < / h1 >
14 < form className = "mt-8 space-y-6" >
15 < div className = "rounded-md shadow-sm -space-y-px" >
16 < div >
17 < label htmlFor = "email" className = "sr-only" > Email address < / label >
18 < input id = "email" name = "email" type = "email" autoComplete = "email" required className = "appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm" placeholder = "Email address" / >
19 < / div >
20 < div >
21 < label htmlFor = "password" className = "sr-only" > Password < / label >
22 < input id = "password" name = "password" type = "password" autoComplete = "new-password" required className = "appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm" placeholder = "Password" / >
23 < / div >
24 < / div >
25 < div >
26 < button type = "submit" className = "group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" >
27 Sign up
28 < / button >
29 < / div >
30 < / form >
31 < p className = "mt-2 text-sm text-gray-600" >
32 Already have an account ? { ' ' }
33 < Link href = "/auth/login" >
34 < a className = "font-medium text-indigo-600 hover:text-indigo-500" > Sign in < / a >
35 < / Link >
36 < / p >
37 < / main >
38 < / div >
39 )
40 }
41
42 export default Register
43
Creating Layout and Navigation Components
Next, let's create a layout component that will wrap our pages and include a common navigation bar.
Layout Component (components/Layout.tsx
)
1 // components/Layout.tsx
2 import { ReactNode } from 'react'
3 import Navbar from './Navbar'
4 import Footer from './Footer'
5
6 interface LayoutProps {
7 children : ReactNode
8 }
9
10 const Layout : React . FC < LayoutProps > = ( { children } ) => {
11 return (
12 < div className = "flex flex-col min-h-screen" >
13 < Navbar / >
14 < main className = "flex-grow" > { children } < / main >
15 < Footer / >
16 < / div >
17 )
18 }
19
20 export default Layout
21