Skip to content

M09 - Next.js

Introduction

Next.js is The React Framework for the Web. It is used by some of the world's largest companies, Next.js enables you to create full-stack web applications by extending the latest React features, and integrating powerful Rust-based JavaScript tooling for the fastest builds.

Following are the key features of Next.js:

  • Routing, A file-system based router built on top of Server Components that supports layouts, nested routing, loading states, error handling, and more..
  • Rendering, Client-side and Server-side Rendering with Client and Server Components. Further optimized with Static and Dynamic Rendering on the server with Next.js. Streaming on Edge and Node.js runtimes.
  • Data Fetching, Simplified data fetching with async/await in Server Components, and an extended fetch API for request memoization, data caching and revalidation.
  • Styling, Support for your preferred styling methods, including CSS Modules, Tailwind CSS, and CSS-in-JS.
  • Optimizations, Image, Fonts, and Script Optimizations to improve your application's Core Web Vitals and User Experience..
  • TypeScript, Improved support for TypeScript, with better type checking and more efficient compilation, as well as custom TypeScript Plugin and type checker.

App Router vs Pages Router

Next.js has two different routers: the App Router and the Pages Router. The App Router is a newer router that allows you to use React's latest features, such as Server Components and Streaming. The Pages Router is the original Next.js router, which allowed you to build server-rendered React applications and continues to be supported for older Next.js applications.

Note

This course material will use App Router. So, remember select that on from the Next.js web side, when you are reading the documentation.

Image 01

Create Next App

The easiest way to get started with Next.js is by using create-next-app. This CLI tool enables you to quickly start building a new Next.js application, with everything set up for you. You can create a new app using the default Next.js template, or by using one of the official Next.js examples.

Create a new project with below command:

1
npx create-next-app@latest

You will be asked a few questions within the project creation. You should select the default values now (you are creating the project first time).

Question Description
What is your project named? › my-app Just type your app name here
Would you like to use TypeScript? › No / Yes Select to use TypeScript or JavaScript
Would you like to use ESLint? › No / Yes ESLint helps to find and fix problems in your JavaScript code.
Would you like to use Tailwind CSS? › No / Yes Look more about Tailwind CSS here.
Would you.. to use src/ directory? › No / Yes Compatibility concession to React projects
Would you.. App Router? › No / Yes We will use App Router in this material.
Would you.. import alias? › No / Yes Helps import files that are in deep level of folder structure.

Image 02

Next your project will be created with needed dependencies.

Run Next App

Just go to your project and run it with npm run dev command.

1
2
cd my-app
npm run dev

And you should see a template page in your browser at http://localhost:3000/.

Image 01

Edit project

Open your project app/page.tsx file and start coding!

Try for example below code:

app/page.tsx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import styles from './page.module.css'

export default function Home() {
  return (
    <main className={styles.main}>
      <h1>Home Page</h1>
      <div>
        <p className={styles.text}>Welcome to Next.js!</p>
      </div>
    </main>
  )
}

And below styles in your page.module.css:

app/page.module.css
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
.main {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  align-items: center;
  padding: 6rem;
}

.text {
  color: yellow;
}

You should see a below in your web browser.

Image 02

Routing

The skeleton of every application is routing. Your app contains a different pages to show to enduser. You can find Next.js Routing materials from here: Routing Fundamentals.

Routing with the app directory is controlled via the folders inside of it. The UI for a particular route is defined by a page.tsx file inside of the folder.

There must be a file that defines the root layout at the top level of the app directory. This layout is applicable to all the routes in the app. In addition, the root layout must define the <html> and the <body> tags because Next.js doesn’t automatically add them.

Now Next.js template has created a following layout.tsx file inside a app folder. This layout will now include page.tsx here as a child inside a <body> element automatically.

app/layout.tsx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'

const inter = Inter({ subsets: ['latin'] })

export const metadata: Metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body className={inter.className}>{children}</body>
    </html>
  )
}

Own App Header

Create a new app/layout.module.css file to have a few CSS styles to use in app/layout.tsx file.

app/layout.module.css
1
2
3
4
5
6
7
8
9
.header {
  width: 100%;
  height: 40px;
  background-color: green;
  text-align: center;
  padding-top: 30px;
  font-weight: bold;
  color:white;
}

Your project should have app/layout.tsx file which defines a UI that is shared across multiple places. A layout can render another layout or a page inside of it. Whenever a route changes to any component that is within the layout, its state is preserved because the layout component is not unmounted.

Modify generated file to use above styles.

app/layout.tsx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import styles from './layout.module.css'

export const metadata = {
  title: 'My JAMK App',
  description: 'Generated by create next app',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <div className={styles.header}>Header from Layout</div>
        <div>{children}</div>
      </body>
    </html>
  )
}

Generated app/page.tsx is your application main file, which will be shown by default. You can edit it how you like.

app/page.tsx
1
2
3
4
5
export default function Home() {
  return (
    <h1>My JAMK App!</h1>
  )
}

Run your code and you should see above header text in your browser.

1
npm run dev

JAMK 01

Profile subpage

Add a new profile folder inside your app folder and create a new page.tsx file inside it.

/app/profile/page.tsx
1
2
3
4
5
export default function Page() {
  return (
    <h1>Profile Page</h1>
  )
}

Save and try now http://localhost:3000/profile route and you should see a profile page.

JAMK 02

Note

See how layout header will be same and only the content is changed.

Create a new layout.tsx file inside your profile folder. This layout file will applies all the routes in this folder.

app/profile/layout.tsx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
export default function Layout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <div className={styles.innerLayout}>
     <div>
        Note: Sample message here! This will be shown later to Lecturer and Student... you can remove this!
      </div>
      <div>
        {children}
      </div>
    </div>)
}

Student subpage

Create a new student folder inside your profile folder and create a new page.tsx file inside it.

app/profile/student/page.tsx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
export default function Page() {
  return (
    <>
      <h1>Student profile</h1>
      <div>Name: Kirsi Kernel</div>
      <div>Age: 23</div>
      <div>Studies: Software, Cyber</div>    
    </>
  )
}

Lecturer subpage

Create a new lecturer folder inside your profile folder and create a new page.tsx file inside it.

app/profile/lecturer/page.tsx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
export default function Page() {
  return (
    <>
      <h1>Lecturer profile</h1>
      <div>Name: Kalle Kanto</div>
      <div>Age: 55</div>
      <div>Lessons: React, Mobile</div>    
    </>
  )
}

Finally modify your app/layout.tsx file to have a link lecturer and student pages.

/app/layout.tsx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Link from 'next/link'
import './globals.css'
import styles from './layout.module.css'

export const metadata = {
  title: 'My JAMK App',
  description: 'Generated by create next app',
}

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <div className={styles.header}>
          <div>Header from Layout</div>
          <div><Link href="/profile/lecturer">Lecturer</Link></div>
          <div><Link href="/profile/student">Student</Link></div>          
        </div>
        <div>{children}</div>
      </body>
    </html>
  )
}

Now your app should display a lecturer page when it is clicked:

JAMK 03

or student page:

JAMK 04

Norris subpage

Let's add one more page to show random Chuck Norris facts. We will explore how data fetching in Next.js 13 differs from earlier versions. All of the components inside the app folder are server components by default.

Create a new norris folder inside your profile folder and create a new page.tsx file inside it.

app/profile/norris/page.tsx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
async function getData() {
  const res = await fetch('https://api.chucknorris.io/jokes/random');
  return res.json();
 }

export default async function Page() {
  const data = await getData();
  return (
    <>
      <h1>Norris profile</h1>
      <div>{data.value}</div>
    </>
  )
}

Add a new route link to Norris page and test it in your app.

JAMK 05

Data fetching

Data fetching is a core part of any application. Next.js extends the native fetch Web API to allow you to configure the caching and revalidating behavior for each fetch request on the server. React extends fetch to automatically memoize fetch requests while rendering a React component tree. You can use fetch with async/await in your application.

Employees

Let's create a small demo which loads an employees data and show it.

Example: Employees

Rendering

Rendering converts the code you write into user interfaces. React and Next.js allow you to create hybrid web applications where parts of your code can be rendered on the server or the client.

Client side components

Client Components are rendered on the client, yes :-). To use a Client Component in Next.js, create a file inside /app and add the 'use client' directive at the top of the file, before any imports. Basicly we have done these kind of components so far in this React based course.

Example: ClickCounter

Note

Next.js recommends using server components until you need to use client components. React hooks, for example useState(), useEffect(), useContext() are only available on the client side.

Server side components

In app directory, which is a newly introduced feature in Next.js 13, server components are the default, meaning all the components and pages are rendered on the server, as long as you specify that the component should be rendered on the client side. Server side components can keep your sensitive information on the server (access tokens, API keys, etc).

Example: Users from MongoDB

Create a MongoDB with a few users

MONGO 01

Create .env.local file to your project root folder and store your MongoDB connection string inside it.

.env.local
1
MONGO_DB_URI="mongodb+srv://USER:PASSWORD@democluster.brlob.mongodb...."

Install mongoose and modify your page.tsx to connect MongoDB.

app/page.tsx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import mongoose, { Schema } from "mongoose";

const schema = new Schema({
  name: { required: true, type: String },
  email: { required: true, type: String },
  age: { required: true, type: Number },
});

const Person = mongoose.models.person || mongoose.model("person", schema, 'persons');

async function dbConnect(): Promise<any> {  
  try {
    const conn = await mongoose.connect(String(process.env.MONGO_DB_URI));
    console.log(`Database connected : ${conn.connection.host}`);
    return conn;
  } catch (err) {
    console.error(err);
  }
}

export default async function Home() {

  await dbConnect();
  const persons = await Person.find({})
  console.log(persons) // This gets printed on the server console

  const items = persons.map( (person:any) => {
    return (
    <div key={person._id}>
      <p>
        Name: {person.name}<br/>
        Email: {person.email}<br/>
        Age: {person.age}
      </p>
    </div>
    )
  })

  return (
    <main>
     {items}
    </main>
  )
}

You should now see your users in a web page.

Tip

Note that you don't need to use Node or Express to get data from MongoDB with Next.js server side components!!

Example: Weather App

Project

Create a new Next.js weather-app project.

1
npx create-next-app@latest

Start your project and test that template is working in your web browser.

1
2
cd weather-app
npm run dev

Weather 01

Dates with date-fns

date-fns provides the most comprehensive, yet simple and consistent toolset for manipulating JavaScript dates.

Install date-fns library to your project.

1
npm install date-fns

Get Weather Forecast

To get weather forecast, you will be using the OpenWeather API. You'll need to create an account in order to acquire an API key.

Note

Open Weather will/may ask you to give credit card information now. You can use any other weather API if you want. For example following Weather by API-Ninjas. You need to modify below programming, if you are using some other API than Open Weather. Just learn returned JSON data and modify UI.

Get your weather forecast API key and store it in your environment variables.

Create .env.local file to your project

.env.local
1
WEATHER_API_KEY='YOUR_API_KEY_HERE'

Modify your page.tsx to load weather forecast.

page.tsx
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import styles from './page.module.css'
import { format } from 'date-fns'

async function getData() {
  const city = 'Jyväskylä';
  const api_key = process.env.WEATHER_API_KEY;
  const url = `http://api.openweathermap.org/data/2.5/weather?q=${city},&appid=${api_key}&units=metric`;
  const weatherRequest = await fetch(url);
  const weatherInfo = await weatherRequest.json();
  console.log('city', city);
  console.log('weatherInfo', weatherInfo);

  if (!weatherRequest.ok) {
    // This will activate the closest `error.js` Error Boundary
    throw new Error('Failed to fetch data')
  }

  return { weatherInfo, city }
}

export default async function Home() {
  const data = await getData()
  return (
    <main className={styles.main}>
      <div className={styles.center}>
          <h1>{data.city}</h1>
          <p>{format(new Date(), 'dd.MM.yyyy HH:mm')}</p>
          <p>{Math.round(data.weatherInfo.main.temp)}°C</p>
          <p>{data.weatherInfo.weather[0].description}</p>
          <img src={`http://openweathermap.org/img/wn/${data.weatherInfo.weather[0].icon}@2x.png`}/>
        </div>
    </main>
  )
}

Save your files and see weather forecast in web browser.

!Weather 03

Deploying Weather App to Vercel

Now it is time to deploy Weather App to Vercel, the platform built by the creators of Next.js.

Note

You need to have a GitHub account for this demo.

Push to GitHub

Before we deploy, let’s push Weather App to GitHub. This will make deployment easier.

  • On your personal GitHub account, create a new repository called weather-app.
  • The repository can be public or private. You do not need to initialize it with a README or other files.

To push to GitHub, you can run the following commands (replace <username> with your GitHub username):

1
2
git remote add origin https://github.com/<username>/weather-app.git
git push -u origin main

Create a Vercel Account

First, go to https://vercel.com/signup to create a hobby Vercel account. Choose Continue with GitHub and go through the sign up process.

Connect GitHub repository

Once you’re signed up, import your weather-app repository on Vercel. You can do so from here: https://vercel.com/import/git.

  • You’ll need to Install Vercel for GitHub. You can give it access to All Repositories.
  • Once you’ve installed Vercel, import weather-app.

Settings

You can use default values for the settings - just change Environment Variables to have your Weather API key. Vercel automatically detects that you have a Next.js app and chooses optimal build settings for you.

Note

Save your WEATHER_API_KEY value without '' chars like f1ece9a26f4bdfd6e......

Deploy

When you deploy, your Next.js app will start building. It should finish in under a minute.

When it’s done, you’ll get deployment URLs. Click on one of the URLs and you should see the Next.js starter page live.

Read more