0% found this document useful (0 votes)
312 views40 pages

How To Create A Full-Stack Application With Next - Js - A Step-By-Step Tutorial For Beginners

This tutorial teaches how to build a full-stack application with Next.js. It covers creating shared layouts, navigation bars, API routes, homepage, dynamic pages, and more. By following the steps, readers will learn Next.js concepts and build a Family Guy character info and quiz app using local JSON data.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
312 views40 pages

How To Create A Full-Stack Application With Next - Js - A Step-By-Step Tutorial For Beginners

This tutorial teaches how to build a full-stack application with Next.js. It covers creating shared layouts, navigation bars, API routes, homepage, dynamic pages, and more. By following the steps, readers will learn Next.js concepts and build a Family Guy character info and quiz app using local JSON data.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 40

Forum Donate

Support our charity and our mission. Donate to freeCodeCamp.org.

MAY 30, 2023 / #NEXT.JS

How to Create a Full-Stack


Application with Next.js – A
Step-By-Step Tutorial for
Beginners
Yazdun Fadali
Forum Donate

Support our charity and our mission. Donate to freeCodeCamp.org.

Next.js can seem intimidating at first, with so many new


concepts to grasp. But don't worry – in this step-by-step
tutorial, I will provide you with all the essential
information you need to create your first modern full-
stack application with Next.js.
In this tutorial, I will take you through the basics of Next.js and guide Donate
Forum
you in creating your very first full-stack application. By the end of this
Support our charity and our mission. Donate to freeCodeCamp.org.
tutorial, you'll have the confidence to start building your own full-
stack applications with Next.js.

So let's jump right in and unlock the power of Next.js together.

Here's what we'll cover:


What Are We Going to Build?

Getting Started

How to create a shared layout in Next.js

How to create a custom navigation bar in Next.js

How to create an API route in Next.js

How to build the homepage

What is the App Router in Next.js?

How to enhance the modularity and maintainability of your


Next.js codebase

How to create a dynamic character's page

How to create dynamic API routes in Next.js

How to create dynamic UI routes in Next.js

What is generateStaticParams in Next.js?

What's the purpose of dynamicParams when generating static


pages in Next.js?

How to generate static pages with generateStaticParams

How to build the quiz section

How to create a client-side component in Next.js


Conclusion Forum Donate

Support our charity and our mission. Donate to freeCodeCamp.org.


Alright, let's dive in!

What Are We Going To Build?


In this tutorial, we will create an engaging app that showcases
information about Family Guy characters. Additionally, we will include
a quiz section where users can test their knowledge of the show.

To keep things simple and familiar for you, we will avoid using a
database and instead utilize local JSON data. By removing the
complexity of database integration, we can focus on mastering the
fundamental concepts of Next.js.

Application Preview

Getting Started
To get started with this tutorial, I highly recommend using the
Forum Donate
provided starter boilerplate that I specifically created for this tutorial.
Support our charity and our mission. Donate to freeCodeCamp.org.
It saves your valuable time by already including the necessary
dependencies and folder structure, eliminating the need to set up your
project from scratch.

Simply clone the starter boilerplate from the GitHub repository and
then follow along with the tutorial. This way, you can focus on learning
and implementing the concepts without getting caught up in setup
details.

Starter Boilerplate: View on GitHub

Final Version: View on GitHub

Once you have set up the starter boilerplate and successfully run it on
your local machine, you should be able to see the initial page. This
page marks the beginning of our tutorial and will serve as the starting
point for our journey.

Boilerplate's initial page

From here, we will gradually build upon the existing code and
implement some cool features into our application. Let's dive in and
get started right away!
How to Create a Shared Layout Forumin Donate

Next.js
Support our charity and our mission. Donate to freeCodeCamp.org.

Often in your applications, you have elements that are shared across
multiple pages, such as a navigation bar or a footer. Manually adding
these elements to each page can be tedious and error-prone.
Fortunately, Next.js provides a convenient way to create shared
layouts that can be reused across our entire application.

The first type of layout is called the Root Layout. As the name
suggests, this layout is shared across all pages in our application. It
serves as the top-most layout and provides a consistent structure for
our entire app. The Root Layout is required and we need to ensure
that it includes the necessary HTML and body tags.

Next, let's consider individual route segments within your application.


Each segment has the option to define its own layout. These layouts,
similar to the Root Layout, will be shared across all pages within that
segment. This allows you to have specific layouts for different sections
of your app, while still maintaining a consistent structure within each
segment.

Now, open up app/layout.js and add the following code to it:

// 📁 app/layout.js
const inter = Inter({ subsets: ['latin'] })

export const metadata = {


title: 'Family Guy',
description: 'Come here and learn more about Family Guy!',
}

export default function RootLayout({ children }) {


return (
<html lang="en">
Forum Donate
<body className={inter.className}>
Support our charity and our mission. Donate to freeCodeCamp.org.
<Navigation />
{children}
</body>
</html>
)
}

The component you see here is the Root Layout component, which
plays a crucial role in creating a shared layout for your entire
application. Let's take a closer look at its structure and functionality.

In the component, you define the metadata object, which contains


default metadata tags for your application. The title property
specifies the title of your application, while the description property
provides a brief description. These metadata tags are important for
search engine optimization (SEO) and can be overridden for specific
routes if needed.

Inside the RootLayout function, you structure the HTML document by


using the html and body tags. You set the lang attribute of the
html tag to "en" to indicate that the content is in English.

Within the body tag, you include the Navigation component, which
is imported from the components directory. This component
represents your navigation bar and will be shared across all pages of
your application. By including it here, you ensure that it is displayed
consistently throughout your app.

The children prop is a special prop that represents the content


rendered within the RootLayout component. This allows you to nest
other components and content within the shared layout.
Forum Donate
Finally, you export the RootLayout component, making it available for
Support our charity
use throughout and our mission. Donate to freeCodeCamp.org.
your application.

How to Create a Custom Navigation


Bar in Next.js
In this section, you will create a simple navigation bar component for
your application. The navigation bar will include a logo and a link that
takes users to the quiz section. Open up components/Navigation.jsx
and add the following code:

// 📁 components/Navigation.jsx
export const Navigation = () => {
return (
<div className="sticky top-0 backdrop-blur-xl bg-[rgba(0,0,0,0.
<Container className="flex justify-between py-5">
<Link href="/">
<Image src="/logo.png" alt="Family Guy" width={70} height
</Link>
<Link
href="/quiz"
className="flex items-center justify-center gap-1 px-5 fo
>
<TbArrowBigRightFilled className="text-lg" />
Take a Quiz
</Link>
</Container>
</div>
)
}

Now you have sticky Navigation component which is shared across


the whole app. If you open up your local server, you should be able to
see the following result: Forum Donate

Support our charity and our mission. Donate to freeCodeCamp.org.

Preview of the sticky navigation bar

Congratulations on your progress so far! You have successfully


created a shared layout with a navigation bar for your Next.js app. This
shared layout ensures consistency across all pages, making it easier to
manage elements like the navigation bar throughout your application.

Now, it's time to focus on building the homepage to display the


characters. In order to display the characters on the Homepage, you
need to create an API route that retrieves all the characters from your
local JSON file, allowing you to dynamically populate the homepage
with the relevant information.

How to Create an API Route in


Next.js
Routing in Next.js is a fundamental concept that determines how
different parts of your application are accessed. When you create a
folder inside the app directory in Next.js, it automatically becomes a
route. But you have the flexibility to define whether it should be a UI
route or an API route.
Naming the file inside the route folder page.jsx transforms it into a Donate
Forum
UI route. This means that it will serve as a regular page with UI
Support our charity and our mission. Donate to freeCodeCamp.org.
components. On the other hand, if you name the file as route.js , it
becomes an API route. This signifies that it will handle API requests
and responses.

It's important to keep in mind that within a single directory, you can
have either a UI route or an API route, but not both. This clear
separation allows for a clean and organized structure while building
your Next.js application.

In the next section, you're going to create your first API route in
Next.js. API routes in Next.js provide a simple and convenient way to
create server-side endpoints within your application.

With API routes, you can define custom routes that handle HTTP
requests and responses, allowing you to fetch or modify data, perform
server-side computations, or integrate with external services.

These routes are written as JavaScript functions that are


automatically deployed as serverless functions in the cloud. API
routes provide a backend-like functionality within your frontend
Next.js app, enabling you to build dynamic and interactive web
applications without the need for a separate server.

How to Build the Homepage


In this section, you will be creating an API route that will enable you to
retrieve all the available characters stored in the local JSON file. By
implementing this API route, you will be able to fetch and display the
characters on the homepage of your application.
How to create the characters API route
Forum Donate
In order to ensure
Support a clean
our charity andseparation
our mission.between your
Donate to API code and UI
freeCodeCamp.org.
code, you will be housing all your API routes within the app/api
directory.

By adopting this approach, you can effectively isolate your API-


related functionality from your user interface, promoting better
organization and maintainability.

This section will guide you through the process of creating the
Characters API route. Simply open up the
app/api/characters/route.js file and add the following code:

// 📁 app/api/characters/route.js
export async function GET() {
return NextResponse.json({ characters: characters.data })
}

In this code snippet, you are importing a JSON file called


characters.json . This file contains data about characters that you
want to use in your application.

Next, you are importing the NextResponse object from the


next/server module. This object provides functions for handling
server responses in a Next.js application.

After that, you define an asynchronous function called GET() . This


function is associated with the HTTP GET request method, which is
commonly used to retrieve data from a server.
Inside the GET() function, you use the NextResponse.json()
Forumfunction
Donate
to construct the server response. You pass an object with a property
Support our charity and our mission. Donate to freeCodeCamp.org.
called characters , which holds the data from the characters.json
file. This response is then returned from the function.

In simpler terms, this code is creating an API route that responds to


GET requests. When a GET request is made to this route, it returns a
JSON response containing the data from the characters.json file.
This allows you to fetch the character data from your application and
use it in other parts of our code.

Now, it's time to test your API route and ensure that everything is
functioning correctly. To make this process simpler, you will use the
browser itself to make the API request. Open your browser and enter
the following URL: http://localhost:3000/api/characters.

Upon doing so, you will be directed to a page where you can observe
the results of the API request. This step allows us to verify that the
API route is working as expected and that it's successfully fetching the
character data:

JSON data in the browser


Here's the JSON data which contains the list of characters. If the
Forum Donate
JSON data looks weird in your browser, make sure to install a JSON
Support our charity and our mission. Donate to freeCodeCamp.org.
Formatter extension on your browser. I'm using Google Chrome so I'm
using this JSON Formatter on my browser.

How to display characters on the homepage


Now that you have your API set up, let's create the UI for our
homepage and display the characters. To do this, open the
app/page.jsx file and add the following code snippet:

// 📁 app/page.jsx
async function getAllCharacters() {
const data = await fetch(`${endpoint}/characters`)

if (!data.ok) {
throw new Error('Failed to fetch data')
}

return data.json()
}

export default async function Page() {


const data = await getAllCharacters()

return (
<main>
<Container className="grid grid-cols-2 gap-1 py-5 md:grid-col
{data?.characters?.map(item => {
return (
<Link
href={`/characters/${item.slug}`}
key={item.name}
className="overflow-hidden rounded-md"
>
<Image
src={item.avatar}
alt=""
className="transition-all duration-500 hover:scale-
width={500}
Forum Donate
height={500}
/>
Support our charity and our mission. Donate to freeCodeCamp.org.
</Link>
)
})}
</Container>
</main>
)
}

In the above code snippet, you have a React component called Page
that is defined as an asynchronous function. This component is
responsible for rendering the homepage UI.

First, you have an asynchronous function called getAllCharacters


which uses the "fetch" function to make an asynchronous HTTP
request to the API endpoint. The response from this request is stored
in the data variable.

Next, you have an error handling check. If the HTTP response


returned an error (status code other than 200), we throw an error
indicating that the data fetch has failed.

Moving on to the Page component, it awaits the result of calling the


getAllCharacters function. The resulting data is stored in the data
variable.

The return statement renders the UI of the homepage. It uses a main


tag as the top-level container and a Container component to hold a
grid layout with multiple columns.

Inside the Container , you map over the characters array in the
data object and generate a list of items. For each character, we create
a "Link" component that serves as a clickable link to a specific
Forum Donate
character's page. The link's URL is generated based on the character's
Support our charity and our mission. Donate to freeCodeCamp.org.
slug property.

Within the Link , you have an Image component that displays the
character's avatar image.

Overall, this code fetches data from an API endpoint, specifically


character data. It then uses this data to dynamically render a grid
layout of character avatars with clickable links to individual character
pages.

Homepage

Your homepage is now looking fantastic, but you may have noticed
something unusual about the way we fetched the data. Typically, you
might be familiar with using the useEffect hook to fetch data from an
API. But in this case, you didn't use any hooks – yet your code is
functioning perfectly.

In the next section, we will take a closer look at what exactly happened
within this component. By examining the code and its execution, you
will gain a deeper understanding of the Next.js mechanisms.

What is The App Router in Next.js?


The App Router in Next.js introduces a new paradigm for developing
applications by leveraging the latest features of React. If you are
already familiar with Next.js, you will discover that the App Router
represents a natural evolution of the existing Pages Router,
Forumwhich is Donate
based on the file system.
Support our charity and our mission. Donate to freeCodeCamp.org.

The App Router basically enables you to run React code on the server
by default, so you are fetching data on the server and only returning
the static HTML to the client. This means that we have a Server
Component that retrieves data from the server and renders its
content on the server side.

There is a caveat to consider: you won't have access to client-side


features like React state and React Hooks inside Server Components,
since they are only running on the server.

If you want to use client-side features, you have to specify that in your
component file by adding "use client" at the top of the file.

What is the point of server side rendering in


Next.js?
In Next.js, SSR allows the server to generate the HTML content of a
webpage and send it to the browser. This means that when you visit a
Next.js website, you don't have to wait for the JavaScript code to load
and execute on the browser before seeing any content. Instead, the
server sends a pre-rendered HTML page, which can be displayed
almost instantly.

The advantage of SSR is that it improves the initial load time of a


webpage, providing a faster and more seamless user experience. It
also helps with search engine optimization (SEO) because search
engines can easily crawl and index the server-rendered HTML
content.
Server-side rendering methods in Next.js
Forum Donate
Next.js provides
Support severaland
our charity methods for rendering
our mission. Donate topages. Each of these
freeCodeCamp.org.
methods serves a specific purpose and can be used in different
scenarios:

Static Site Generation (SSG): Static Generation is a server-side


rendering method where Next.js generates HTML at build
time. During the build process, Next.js fetches data from APIs
or other data sources and pre-renders the HTML pages. These
pre-rendered pages can then be served to the client upon
request. SSG is suitable for websites with content that doesn't
frequently change.

Server-Side Rendering (SSR): Server-Side Rendering is


another method where Next.js generates HTML on each
request. When a user visits a page, Next.js fetches the data and
renders the HTML on the server before sending it to the client.
SSR is useful for websites with frequently updated content or
personalized user experiences.

Incremental Static Regeneration (ISR): ISR is a feature in


Next.js that allows you to statically generate pages on-demand,
rather than at build time. This means that your site can be both
statically generated and dynamic at the same time.

Now that we have a better understanding of server side rendering in


Next.js, we can move on to the next section.

How to Enhance the Modularity and


Maintainability of Your Next.js
Codebase
In order to avoid code repetition and enhance code reusability,
Forum you Donate
can adopt a modular approach in your Next.js project. By isolating
Support our charity and our mission. Donate to freeCodeCamp.org.
commonly used functions like getAllCharacters in a separate
module, you can conveniently access and reuse them in multiple parts
of your codebase.

You can make a quick adjustment in your project. First, navigate to the
app/page.jsx file and locate the getAllCharacters function at the
top. Cut out this function from the file.

Next, open the lib/characters.js file and export the


getAllCharacters function from there. By moving the function to a
separate module, you can easily import and use it in different parts of
your codebase:

// 📁 lib/characters.js
import { endpoint } from '@/utils/endpoint'

export async function getAllCharacters() {


const data = await fetch(`${endpoint}/characters`)

if (!data.ok) {
throw new Error('Failed to fetch data')
}

return data.json()
}

Now let's import the getAllCharacters function from


lib/characters.js and use it inside app/page.jsx :
// 📁 app/page.jsx Forum Donate

Support our charity and our mission. Donate to freeCodeCamp.org.


import { getAllCharacters } from '@/lib/characters'

export default async function Page() {


const data = await getAllCharacters()

return (
<main>
//content went here ...
</main>
)
}

This way, you are going to have access to this fetch function
throughout your whole codebase.

How to Create a Dynamic


Character's Page
Congratulations on reaching this point in the tutorial! By now, you
have gained a solid understanding of the fundamentals of Next.js.

In this section, you will be creating a dynamic API route. This route will
enable you to fetch data for each character individually and
subsequently build a user interface (UI) to showcase these characters
to your users.

How to create dynamic API routes in Next.js


By creating a dynamic API route in Next.js, you can fetch character
data based on the character's slug. To achieve this, you need to use
brackets to name your folders, indicating to Next.js that it is a dynamic
route. By naming the folders accordingly, you can access this dynamic
value within your code, allowing you to retrieve and display the
Forum Donate
desired character's data.
Support our charity and our mission. Donate to freeCodeCamp.org.

Open up api/characters/[slug]/route.js and add the following


snippet:

// 📁 api/characters/[slug]/route.js
export async function GET(req, { params }) {
try {
const character = characters.data.find(item => item.slug === pa

if (!character) {
return new NextResponse('not found', { status: 404 })
}

const character_qoutes = qoutes.data.filter(


item => item.character_id === character.id,
)

return NextResponse.json({
character,
character_qoutes: character_qoutes.length > 0 ? character_qou
})
} catch (error) {
return new NextResponse('Internal Server Error', { status: 500
}
}

In the above code snippet, you have an asynchronous function named


GET that handles a GET request in a Next.js API route. Let's break it
down step by step:

1. You import the characters and quotes data from their


respective JSON files using the Next.js file system
( @/data/characters.json and @/data/quotes.json ).
2. The function receives two parameters: req (representing
Forumthe Donate
incoming request) and an object called params which contains
Support our charity and our mission. Donate to freeCodeCamp.org.
the dynamic parameters extracted from the request URL.

3. Inside a try-catch block, the code attempts to find a character


in the characters data by comparing the slug parameter
from params with the slug property of each character object.

4. If no character is found, the code returns a "not found"


response with a status code of 404 using the NextResponse
class from the next/server package.

5. If a character is found, the code proceeds to filter the quotes


data array based on the character_id property matching the
found character's id .

6. The filtered character quotes are assigned to the


character_quotes variable.

7. Finally, the code returns a JSON response using


NextResponse.json() , including the character object and the
character_quotes array (or null if no quotes are found).

Next.js automatically extracts dynamic parameters from the URL and


makes them available in the params object. In this code, you access
the slug parameter using params.slug . This allows you to retrieve
the specific character's slug from the URL and use it to find the
corresponding character in the characters data.

Now you can test this endpoint to see the result, open up
http://localhost:3000/api/characters/peter-griffin in your browser
and you should be able to see the following JSON data:
Forum Donate

Support our charity and our mission. Donate to freeCodeCamp.org.

JSON data in the browser

How to create dynamic UI routes in Next.js


Now that your API is set up and capable of fetching character data, it's
time to create a dynamic UI page to showcase this data.

The process of creating a dynamic UI page is quite similar to what you


did when setting up the dynamic API route. But this time, you will be
using page.jsx instead of route.js to generate a UI route.

Open up app/characters/[slug]/page.jsx and add the following


snippet:

// 📁 app/characters/[slug]/page.jsx
import { getAllCharacters } from '@/lib/characters'

export const dynamicParams = false

export async function generateStaticParams() {


const { characters } = await getAllCharacters()
return characters.map(character => ({ slug: character.slug }))
}

export async function getCharacterBySlug(slug) {


const data = await fetch(`${endpoint}/characters/${slug}`)

if (!data.ok) {
throw new Error('Failed to fetch data')
}

return data.json()
}
Forum Donate
export default async function Page({ params }) {
Support our charity and our mission. Donate to freeCodeCamp.org.
const { character, character_qoutes } = await getCharacterBySlug(

return (
<Container className="flex flex-col gap-5 py-5" as="main">
<div className="flex flex-col gap-2">
<h1 className="text-2xl font-semibold capitalize">{characte
<ul className="flex gap-1 text-sm">
{character.occupations.map(item => {
return (
<li
key={item}
className="p-2 text-gray-300 bg-gray-800 rounded-md
>
{item}
</li>
)
})}
</ul>
</div>
<p className="text-sm leading-6">{character.description}</p>
<ul className="grid gap-2 sm:grid-cols-2">
{character.images.map(image => {
return (
<li
key={image}
className="relative flex overflow-hidden bg-gray-900
>
<Image
className="transition-all duration-500 hover:scale-
src={image}
alt=""
width={760}
height={435}
/>
</li>
)
})}
</ul>
{character.skills && (
<>
<h2 className="text-xl font-bold">Power and Skills</h2>
<ul className="flex flex-wrap gap-1">
{character.skills.map(item => {
return (
<li
Forum Donate
className="flex justify-center flex-grow px-2 py-
Support our charity and our mission. Donate to freeCodeCamp.org.
key={item}
>
{item}
</li>
)
})}
</ul>
</>
)}
{character_qoutes && (
<>
<h2 className="text-xl font-bold">Famous Qoutes</h2>
<ul className="grid gap-5">
{character_qoutes.map((item, idx) => {
return (
<li
className="p-2 italic text-gray-400 border-l-4 bo
key={item.idx}
>
{item.qoute}
</li>
)
})}
</ul>
</>
)}
</Container>
)
}

Don't be intimidated by the length of the code you see here! It may
seem overwhelming at first, but it's actually quite straightforward.
Let's take a deeper look at what we did in this code:

What is generateStaticParams in Next.js?


In Next.js, the generateStaticParams function is used to specify the
dynamic routes that should be pre-rendered at build time.
Forumwith Donate
To explain it in simpler terms, let's imagine you have a website
multiple blogour
Support posts, andand
charity each
ourblog postDonate
mission. has a unique URL. With
to freeCodeCamp.org.
generateStaticParams , you can tell Next.js which blog post URLs
should be generated and pre-rendered during the build process.

When you implement generateStaticParams , you provide it with a


function that returns an array of objects representing the dynamic
paths you want to pre-render.

Each object typically contains a parameter that corresponds to the


dynamic portion of the URL. For example, if your blog posts have URLs
like /blog/post-1 , /blog/post-2 , and so on, you would return an
array with objects like { params: { slug: 'post-1' } } , { params:
{ slug: 'post-2' } } , and so on.

In our case, we are retrieving a list of characters using the


getAllCharacters() function. Then we map over the characters and
return an array of objects, each containing a slug property with the
character's slug value.

Next.js will then use this information to generate the static HTML files
for these paths during the build process. This allows the pages to be
served as static files, improving performance and SEO.

What's the purpose of dynamicParams when


generating static pages in Next.js?
In Next.js, the behavior of dynamic segments that were not generated
using generateStaticParams is controlled by the dynamicParams .

When dynamicParams is set to true , Next.js will attempt to fetch the


corresponding page dynamically when a dynamic segment is visited.
Forum Donate
On the other hand, if dynamicParams is set to false , Next.js will
return a 404our
Support page if it fails
charity and to
ourfind the requested
mission. page.
Donate to freeCodeCamp.org.

This setting allows you to define how Next.js handles dynamic


segments that were not pre-generated, providing flexibility in
handling dynamic routes in your applications.

How to generate static pages with


generateStaticParams
Now that you've successfully generated a static path for each
character, let's see how you can fetch data for each character.

The getCharacterBySlug function is an asynchronous function that


takes the slug parameter, fetches data from the specified API
endpoint using fetch , and returns the response data in JSON format.
If the response is not successful ( !data.ok ), an error is thrown.

The Page component receives the params object as a prop, which


contains the dynamic parameter values extracted from the URL. It
calls the getCharacterBySlug function, passing the character's slug
extracted from params to fetch the specific character's data.

The returned data is then used to populate the UI, which includes
displaying the character's name, occupations, description, images,
power and skills (if available), and famous quotes (if available).

Ideally, you can put getCharacterBySlug inside lib/characters.js


and export it from there, but that's up to you to decide!
Forum Donate

Support our charity and our mission. Donate to freeCodeCamp.org.

Our application so far

How to Build the Quiz Section


Congratulations on reaching this point in the tutorial! You've
accomplished a lot by creating dynamic API routes and UI pages, and
understanding the different rendering methods in Next.js.

Now, let's add a touch of interactivity to this application. In this


section, you will be building an engaging quiz section where users can
put their Family Guy knowledge to the test.

How to create an API route to retrieve random


questions
To ensure an exciting and unique experience for each user, it's
important to avoid repeating the same question in the quiz every time.
We want to keep things fresh and engaging.

In order to achieve this, we'll implement a mechanism that presents


users with different questions each time they start the quiz.

Open up app/api/quiz/random/route.js and add the following


snippet:

// 📁 app/api/quiz/random/route.js
export async function GET() {
try {
Forum
const random = Math.floor(Math.random() * questions.data.length
Donate
return NextResponse.json({
Support our charity and our mission. Donate to freeCodeCamp.org.
randomQuestion: questions.data[random].id,
})
} catch (error) {
return new NextResponse('Internal Server Error', { status: 500
}
}

In this Next.js API route, you are implementing the logic to fetch a
random question from a set of questions stored in a JSON file called
quiz.json . First, we import the questions data from the JSON file
and the NextResponse object from the Next.js server package.

Inside the GET function, we generate a random number using the


Math.random() and Math.floor() functions. This number is used to
select a random question from the questions.data array. We retrieve
the question using its index, and specifically the id property of the
randomly selected question.

Now let's create a UI to make use of this random question.

How to create an intro page for the Quiz


You will now create an user interface (UI) for the quiz introduction
section. This UI will serve as the initial screen users see before starting
the quiz.

You will leverage the API route you just created to dynamically
redirect users to a new question each time they start the quiz.

Let's open up app/quiz/page.jsx and add the following code:


// 📁 app/quiz/page.jsx Forum Donate

Support our charity and our mission. Donate to freeCodeCamp.org.


export async function getRandomQuizQuestion() {
const data = await fetch(`${endpoint}/quiz/random`, { cache: 'no-

if (!data.ok) {
throw new Error('Failed to fetch data')
}

return data.json()
}

export default async function Page() {


const data = await getRandomQuizQuestion()

return (
<Container
as="main"
className="flex flex-col gap-5 py-5 md:flex-row-reverse md:ju
>
<div className="relative overflow-hidden rounded-2xl">
<div className="md:w-[24rem]">
<Image src="/wallpaper.jpg" alt="" width={700} height={70
</div>
<div className="absolute top-0 bottom-0 left-0 right-0 bg-g
</div>

<div className="md:w-[50%] flex flex-col gap-5">


<h1 className="text-2xl font-semibold">Family Guy Quiz</h1>
<p className="text-sm leading-6 text-gray-300">
Take this quiz to find out how much you know about the hi
sitcom Family Guy. Test your knowledge of the characters,
episodes, and the show&apos;s many pop culture references
</p>
<Link
href={`/quiz/${data.randomQuestion}`}
className="flex items-center justify-center gap-1 px-5 py
>
<TbArrowBigRightFilled className="text-lg" />
Take a Quiz Now!
</Link>
</div>
</Container>
)
}
Forum Donate

Support our charity and our mission. Donate to freeCodeCamp.org.

This code sets up the UI for the quiz introduction section, fetches a
random question from the API, and provides a button for users to
start the quiz.

In the this code, you might have noticed a change where we pass a
parameter to the fetch method: { cache: 'no-store' } .

This change is significant because it ensures that the page we are


working on will not be generated statically using the Static Site
Generation (SSG) method. Instead, it will make an API request to the
provided endpoint and fetch fresh data each time users visit the page.

By using { cache: 'no-store' } , we disable caching for this specific


request. This guarantees that every time a user accesses this page, a
new question will be fetched.

This approach adds a dynamic and interactive element to the quiz


experience, ensuring that users always encounter a different question
each time they visit the page.

Quiz introduction page


How to create dynamic API route for Forum quiz Donate
questions
Support our charity and our mission. Donate to freeCodeCamp.org.
To provide dynamic quiz questions, you need to create a new API
route that will fetch and return the quiz questions. This way, you can
retrieve the questions dynamically and present them to the users.

Open up app/api/quiz/[id] and add the following code:

// 📁 app/api/quiz/[id]
export async function GET(req, { params }) {
try {
const question = questions.data.find(item => item.id === params

if (!question) {
return new NextResponse('not found', { status: 404 })
}

const { correct_answer, ...rest } = question

return NextResponse.json({
question: rest,
})
} catch (error) {
return new NextResponse('Internal Server Error', { status: 500
}
}

In this Next.js route, you handle a GET request to retrieve a specific


question from a quiz. You import the questions data from a JSON file.
Using the provided ID in the request parameters, you search for a
matching question. If the question is not found, you return a "Not
Found" response with a status code of 404.
If the question is found, you extract the correct answer and store the Donate
Forum
remaining question details in the rest variable.
Support our charity and our mission. Donate to freeCodeCamp.org.

Finally, you return a JSON response containing the question details


(excluding the correct answer). If any errors occur during the process,
you return an "Internal Server Error" response with a status code of
500.

Now you can test this API route in your browser by opening up your
local server http://localhost:3000/api/quiz/CfQnf3lH56:

http://localhost:3000/api/quiz/CfQnf3lH56

How to create a dynamic API route to fetch


answers
Before implementing the UI, let's create an API route where you can
fetch the correct answer for each question.

Open up app/api/quiz/answer/[id]/route.js and add the following


code to it:

// 📁 app/api/quiz/answer/[id]/route.js
export async function GET(req, { params }) {
try {
Forum Donate
const question = questions.data.find(item => item.id === params

Support our charity and our mission. Donate to freeCodeCamp.org.


if (!question) {
return new NextResponse('not found', { status: 404 })
}

const { correct_answer } = question

const filteredQuestions = questions.data.filter(


item => item.id !== params.id,
)
const random = Math.floor(Math.random() * filteredQuestions.len

return NextResponse.json({
correct: correct_answer,
random: filteredQuestions[random].id,
})
} catch (error) {
return new NextResponse('Internal Server Error', { status: 500
}
}

The purpose of this API route is to retrieve a specific question from a


quiz based on the provided ID. The code searches for the question by
comparing the given ID with the IDs of questions stored in the
questions data. If the requested question is found, its correct answer
is extracted.

To suggest the next question, the code removes the current question
from the available pool of questions by filtering it out. It then
generates a random index within the range of the remaining questions.
Using this random index, a new question is selected as a suggestion for
the next question.

The code constructs and returns a JSON response containing the


correct answer for the requested question and the ID of the randomly
chosen next question. This functionality allows users to retrieve
specific quiz questions and receive suggestions for the following
Forum Donate
question, improving the interactive experience of the quiz.
Support our charity and our mission. Donate to freeCodeCamp.org.

How to create dynamic UI routes for the Quiz


questions
Now that you have successfully built all the necessary API endpoints,
it's time to take the next step and create a user interface (UI) that
allows users to interact with the APIs you've developed. This UI will
serve as the gateway for users to access and utilize the functionalities
offered by your APIs.

In this section, you'll learn about dynamic server-side rendering (SSR)


in Next.js. We've already covered static pages (SSG), and now you'll
explore SSR. Also SSR is much easier to implement.

Open up app/quiz/[id]/page.jsx and add the following code:

// 📁 app/quiz/[id]/page.jsx
async function getQuizQuestion(id) {
const data = await fetch(`${endpoint}/quiz/${id}`)

if (!data.ok) {
throw new Error('Failed to fetch data')
}

return data.json()
}

export default async function Page({ params }) {


const { question } = await getQuizQuestion(params.id)

return (
<Container as="main" className="flex flex-col gap-5 py-5">
<h1 className="text-lg font-semibold">{question.title}</h1>
<Answer answers={question.answers} questionId={params.id} />
</Container>
Forum Donate
)
Support our charity and our mission. Donate to freeCodeCamp.org.
}

This Next.js page component fetches question data from an API


endpoint using the getQuizQuestion function. It then renders the
question's title and the corresponding answers using JSX components.

Question UI route

That's all you needed to do to render a Next.js page server side!

In the next section, you're going to create a client side component to


handle user's interaction with the answers.

How to create a client-side component in


Next.js
In the final section of this tutorial, you'll create a client-side
component to handle a user's interactions with the answers.

Open up components/Answer.jsx and add the following code:

// 📁 components/Answer.jsx
'use client'

import { useEffect, useState } from 'react'


import cn from 'classnames'
import Link from 'next/link'
Forum Donate
import { FiRepeat } from 'react-icons/fi'
Support our charity and our mission. Donate to freeCodeCamp.org.
import { MdNearbyError } from 'react-icons/md'
import { FaCheck } from 'react-icons/fa'

export const Answer = ({ answers, questionId }) => {


const [selected, setSeleceted] = useState(null)
const [data, setData] = useState(null)
const [loading, setLoading] = useState(false)

useEffect(() => {
let subscribed = true
if (selected) {
setLoading(true)
fetch(`/api/quiz/answer/${questionId}`)
.then(res => res.json())
.then(data => {
setLoading(false)
if (subscribed) {
setData(data)
}
})
}

return () => {
console.log('cancelled!')
subscribed = false
}
}, [questionId, selected])

return (
<>
<ul className="grid grid-cols-2 gap-2 md:grid-cols-4">
{answers.map(item => {
const isLoading = selected === item && loading
const isWrong =
selected === item && data && data?.correct !== selected
const isCorrect = data?.correct === item

return (
<li key={item}>
<button
disabled={data || loading}
onClick={() => setSeleceted(item)}
className={cn(
'p-2 rounded-md items-center justify-between w-f
Forum Donate
isLoading && 'animate-pulse',
isWrong ? 'bg-red-700' : 'bg-slate-800',
Support our charity and our mission. Donate to freeCodeCamp.org.
isCorrect && 'outline text-green-500',
)}
>
{item}
{isCorrect && <FaCheck />}
{isWrong && <MdNearbyError />}
</button>
</li>
)
})}
</ul>
{data?.random && (
<Link
href={`/quiz/${data.random}`}
className="flex items-center gap-1 text-blue-400"
>
<FiRepeat className="mt-1" />
Do it again
</Link>
)}
</>
)
}

This is a React component that takes two props: answers and


questionId . It sets up state using the useState hook to keep track of
the selected answer, the fetched data, and the loading status.

Inside the component, there's a useEffect hook that runs whenever


the questionId or selected value changes. If a selected answer
exists, it makes an API request to fetch the corresponding data using
fetch and updates the state accordingly.

The component renders a list of answer options using a map function.


Each answer option is represented as a button. The button's
appearance is modified based on the selected answer, loading status,
and correctness of the answer. It also displays different icons,
Forumsuch asDonate
a checkmark or an error icon, based on the correctness of the selected
Support our charity and our mission. Donate to freeCodeCamp.org.
answer.

Additionally, if the fetched data includes a random property, a link is


rendered to repeat the quiz with a new random question.

Here is how the final version of our quiz looks like:

Quiz final version

Conclusion
Here is the end! You've successfully built your first full-stack
application using Next.js. Throughout this step-by-step tutorial, you
learned the basics of Next.js, exploring its powerful features and
gaining the necessary knowledge to create modern web applications.

Through this tutorial, you not only built a functional app, but you also
gained the confidence to start creating our own full-stack applications
with Next.js. You learned about routing, server-side rendering, API
integration, and more.

Now that you have a solid foundation in Next.js, the possibilities are
endless. You can continue exploring advanced topics, such as database
integration, authentication, and deployment, to take your applications
to the next level.
You can follow me on Twitter where I share more useful Forum
tips on web Donate
development. Happy coding!
Support our charity and our mission. Donate to freeCodeCamp.org.

Yazdun Fadali
Frontend developer, With passion for creating cutting-edge digital
experiences and a talent for crafting smooth, intuitive user interfaces !

If you read this far, thank the author to show them you care.
Say Thanks

Learn to code for free. freeCodeCamp's open source curriculum has


helped more than 40,000 people get jobs as developers.
Get started
ADVERTISEMENT

freeCodeCamp is a donor-supported tax-exempt 501(c)(3) charity organization (United States


Federal Tax Identification Number: 82-0779546)
Our mission: to help people learn to code for free. We accomplish this by creating
Forum thousands of
Donate
videos, articles, and interactive coding lessons - all freely available to the public.
Support our charity and our mission. Donate to freeCodeCamp.org.
Donations to freeCodeCamp go toward our education initiatives, and help pay for servers,
services, and staff.

You can make a tax-deductible donation here .

Trending Guides

Date Formatting in JS Java Iterator Hashmap Cancel a Merge in Git


What is a Linked List? Install Java in Ubuntu Python Ternary Operator
Full Stack Career Guide Python Sort Dict by Key Smart Quotes Copy/Paste
JavaScript Array Length Sets in Python Kotlin vs Java
SQL Temp Table HTML Form Basics Comments in YAML
Pandas Count Rows Python End Program Python XOR Operator
Python Dict Has Key Python List to String Exit Function in Python
String to Array in Java Python Import from File Parse a String in Python
Python Merge Dictionaries Copy a Directory in Linux Reactive Programming Guide
Center Text Vertically CSS What’s a Greedy Algorithm? Edit Commit Messages in Git

Mobile App

Our Charity

About Alumni Network Open Source Shop Support Sponsors Academic Honesty

Code of Conduct Privacy Policy Terms of Service Copyright Policy

You might also like