Pre-requisites

  • JavaScript
  • React Fundamentals

Introduction?

Next.js is a framework of React for creating pre-rendered React based websites. It adds additional optimization capabilities like server-side rendering (SSR) and static-site generation (SSG). Pre-rendering improves performance and also results in better SEO than traditional single-page applications because the response from the server is a fully rendered HTML page rather than just a blank one which is what would be returned in a regular React application without Next.js. So in the eyes of search engine crawlers, it makes our site much more palatable.

Features of Next.js

Following are the few main features of Next.js

Hot Reloading

Next.js reloads the page whenever it detects any change saved to disk. This helps developers view live changes without manually reloading the app.

Automatic Routing

The pages folder contains the React Components and the route corresponding to each component is created automatically.

Server-Side Rendering

You can render React components on the server side before sending the HTML to the client.

Ecosystem Compatibility

Next.js plays well with the rest of the JavaScript, Node, and React ecosystem.

Automatic Code Splitting

Pages are rendered with just the libraries and JavaScript that they need, no more. Instead of generating one single JavaScript file containing all the app code, the app is broken up automatically by Next.js in several different resources. Only required Javascript for the page is loaded. Next.js does that by analyzing the resources imported. For example, if only one of your components imports the moment library, that specific component will include the library in its bundle. This ensures your first-page load is as fast as it can be, and only future page loads (if they will ever be triggered) will send the JavaScript needed to the client. Notable exception: Frequently used imports are moved into the main JavaScript bundle if they are used in at least half of the site pages.

Pre-fetching

The Link component, used to link together different pages, supports a prefetch prop which automatically pre-fetches page resources (including code missing due to code splitting) in the background.

Dynamic Components

You can import JavaScript modules and React Components dynamically.

Static Exports

Using the next export command, Next.js allows you to export your app to static HTML, which can be run standalone without the need of a Node.js server.The exported app supports almost every feature of Next.js, including dynamic routes, prefetching, preloading and dynamic imports.

TypeScript Support

Next.js is written in TypeScript and as such comes with excellent TypeScript support.

Folder Structure

Every Next.js project has 3 main folders: pages, public, and styles.

pages

In Next.js, a page is a React Component exported from a file in the pages directory.

Pages are associated with a route based on their filename. For example, in development:

  • pages/index.js is associated with the / route.
  • pages/posts/first-post.js is associated with the /posts/first-post route.

Some pages, like _app.js, include an underscore prefix in their name to mark them as custom components. These components are used by Next.js to work with other components.

For example, _app.js is used to start each page and is not used as its webpage.

public

This folder is for static file serving, meaning these files do not change and can only be referenced. This folder often contains images or icons that the site uses as well as internal information like Google Site Verifications etc.

styles

This folder contains our CSS style sheets, which determine the appearance of all of our page elements. The globals.css file sets the general standard that all pages in the project will use.

You can also add component-specific styling using module files named with a .module suffix, <componentName>.module.css.

Data Fetching With Next.js

Data fetching is when Next.js requests data from a server to generate a page. Choosing the right pre-render methods and fetch functions is essential to making user-friendly apps. As mentioned above that Next.js has two forms of pre-rendering: static site generation and server-side rendering. Let’s see data fetching strategies for each case.

  • SSR: Better for highly interactive or rapidly changing pages that don’t work with static generation.
  • SSG: Better for text-only pages or pages that do not change because the static render will always meet the needs of the user.

You can use one or the other fetching method, or you can implement a hybrid system. Next.js has 3 async data fetching functions that act as centralized fetching alternatives to the traditional React method. These functions are:

  • getStaticProps: used with SSG to pull page content from external data.
  • getStaticPaths: used with SSG to pull page paths from external data.
  • getServerSideProps: used with SSR to pull pre-rendered pages at build time.

Getting Started

Pre-requiste

  • Node v14.15.5 or higher (follow this link to install)

Sample Project

After having a brief introduction about what Next.js is, let’s start coding. The project we are going to develop is Search Prayer Timings. In this, we will hit a third-party API and pass city and country to it. It will return details regarding prayer timings.

Create Next.js app

To create a Next app enter npx create-next-app <app-name> into your command prompt.

npx create-next-app prayer-timings

This creates a next app named prayer-timings with a built-in folder structure. In the project folder, run :

npm run dev

This starts your app at http://localhost:3000/

The next app has been created, let’s customize it!

Open this folder in your favorite code editor. Remove everything from index.js and write the following code to create a component containing two input fields (to enter city and country) and a search button. Upon click, the api will be called and a table will be generated according to the response we get.

Note: ?. refers to optional chaining in the following code.

import React, { useState, useEffect } from "react";
import Link from "next/link";

const Index = () => {
  const [cityName, setCityName] = useState("");
  const [countryName, setCountryName] = useState("");
  const [result, setResult] = useState([]);

  const handleSearch = (e) => {
    e?.preventDefault();

    fetch(
      `http://api.aladhan.com/v1/timingsByCity?city=${cityName}&country=${countryName}&method=8`
    )
      .then((response) => response?.json())
      .then((data) => {
        setResult([...result, data]);
      })
      .catch(() => {
        console.log("Request Failed");
      });
  };

  return (
    <>
      <div className="page-wrapper bg-color-1">
        <div className="navbar">
          <Link href="/">
            <a>Home {""}</a>
          </Link>
          <Link href="/about">
            <a>About Author</a>
          </Link>
        </div>
        <div className="title-text"> Search Prayer Timings</div>
        <div className=" p-b-120">
          <div className="wrapper display-flex">
            <div className="card card-7">
              <div className="card-body">
                <form className="form" method="POST" action="#">
                  <div className="input-group input--large">
                    <label className="label">City Name</label>
                    <input
                      className="input--style-1"
                      type="text"
                      placeholder="Lahore, Islamabad..."
                      name="going"
                      value={cityName}
                      onChange={(e) => {
                        setCityName(e?.target?.value);
                      }}
                    />
                  </div>

                  <div className="input-group input--medium">
                    <label className="label">Country Name</label>
                    <input
                      className="input--style-1"
                      type="text"
                      name="checkout"
                      placeholder="Pakistan, UK..."
                      id="input-end"
                      value={countryName}
                      onChange={(e) => {
                        setCountryName(e?.target?.value);
                      }}
                    />
                  </div>

                  <button
                    className="btn-submit"
                    type="submit"
                    onClick={(e) => {
                      handleSearch(e);
                    }}
                  >
                    search
                  </button>
                </form>
              </div>
            </div>
          </div>

          {result?.length !== 0 ? (
            <div className="table-container">
              <table className="styled-table">
                <thead>
                  <tr>
                    <th>Gregorian Date</th>
                    <th>Hijri</th>
                    <th>TimeZone</th>
                    <th>Timings</th>
                  </tr>
                </thead>
                <tbody>
                  {result?.map((res) => {
                    return (
                      <>
                        <tr>
                          <td>{res?.data?.date?.gregorian?.date}</td>
                          <td>{res?.data?.date?.hijri?.date}</td>
                          <td>{res?.data?.meta?.timezone}</td>
                          <td>
                            <table className="styled-table">
                              <thead>
                                <tr>
                                  <th>Fajr</th>
                                  <th>Duhr</th>
                                  <th>Asr</th>
                                  <th>Maghrib</th>
                                  <th>Isha</th>
                                </tr>
                              </thead>
                              <tbody>
                                <tr>
                                  <td>{res?.data?.timings?.Fajr}</td>
                                  <td>{res?.data?.timings?.Dhuhr}</td>
                                  <td>{res?.data?.timings?.Asr}</td>
                                  <td>{res?.data?.timings?.Maghrib}</td>
                                  <td>{res?.data?.timings?.Isha}</td>
                                </tr>
                              </tbody>
                            </table>
                          </td>
                        </tr>{" "}
                      </>
                    );
                  })}
                </tbody>
              </table>
            </div>
          ) : (
            ""
          )}
        </div>
      </div>
    </>
  );
};

export default Index;

Now create a new file in pages directory as about.js and enter the following code:

import React from "react";
import Link from "next/link";
import Image from "next/image";

const about = () => {
  return (
    <>
      <div className="navbar">
        <Link href="/">
          <a>Home {""}</a>
        </Link>
        <Link href="/about">
          <a>About Author</a>
        </Link>
      </div>

      <section className="heading">
        <a>
          <Image
            priority
            src="/images/profile.jpg"
            className="borderCircle"
            height={100}
            width={130}
            alt="name"
          />
        </a>
        <p>
          I am an Associate Software Engineer with hands-on experience in
          ReactJS.
        </p>
        <p>
          (This is a sample website - you’ll be building a site like this on{" "}
          <a href="https://nextjs.org/learn">our Next.js tutorial</a>.)
        </p>
      </section>
    </>
  );
};

export default about;

Remove everything from App.css and add the following code for better representation.

button {
  outline: none;
  background: none;
  border: none;
  font-family: inherit;
}

/* ==========================================================================
     #PAGE WRAPPER
     ========================================================================== */

body {
  font-family: "Roboto", "Arial", "Helvetica Neue", sans-serif;
  font-weight: 400;
  font-size: 14px;
  margin: unset;
  height: 100vh;
  width: 100%;
  background: #ffece0;
}

/* ==========================================================================
     #BACKGROUND
     ========================================================================== */

.bg-color-1 {
  background: #ffece0;
}

/* ==========================================================================
     #BUTTON
     ========================================================================== */
.btn-submit {
  display: inline-block;
  line-height: 80px;
  font-family: inherit;
  background: #ffe151;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  border-radius: 10px;
  text-transform: capitalize;
  color: #333;
  font-size: 18px;
  -webkit-transition: all 0.4s ease;
  -o-transition: all 0.4s ease;
  -moz-transition: all 0.4s ease;
  transition: all 0.4s ease;
  padding: 0 30px;
  -webkit-box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
  -moz-box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
  box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
}

.btn-submit:hover {
  background: #ebcd3d;
}

/* ==========================================================================
     #FORM
     ========================================================================== */
input,
textarea {
  outline: none;
  margin: 0;
  border: none;
  -webkit-box-shadow: none;
  -moz-box-shadow: none;
  box-shadow: none;
  width: 100%;
  font-size: 18px;
  font-family: inherit;
}

input:disabled {
  cursor: pointer;
  background: transparent;
}

button {
  cursor: pointer;
}

.input-group {
  position: relative;
  margin-bottom: 23px;
  -webkit-box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
  -moz-box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
  box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
  padding: 16px 20px;
  padding-bottom: 10px;
  background: #fff;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  border-radius: 10px;
  float: left;
  margin-right: 10px;
}

.form::after {
  content: "";
  clear: both;
  display: table;
}

.label {
  font-size: 12px;
  color: #999;
  text-transform: uppercase;
  display: block;
  font-weight: 700;
}

.input--large {
  width: 300px;
}

.input--medium {
  width: 190px;
}

@media (max-width: 767px) {
  .input--large,
  .input--medium {
    width: 100%;
  }
}

.input--style-1 {
  font-size: 18px;
  padding: 9px 0;
  color: #666;
  font-family: "Roboto", "Arial", "Helvetica Neue", sans-serif;
}

.input--style-1::-webkit-input-placeholder {
  /* WebKit, Blink, Edge */
  color: #333;
}

.input--style-1:-moz-placeholder {
  /* Mozilla Firefox 4 to 18 */
  color: #333;
  opacity: 1;
}

.input--style-1::-moz-placeholder {
  /* Mozilla Firefox 19+ */
  color: #333;
  opacity: 1;
}

.input--style-1:-ms-input-placeholder {
  /* Internet Explorer 10-11 */
  color: #333;
}

.input--style-1:-ms-input-placeholder {
  /* Microsoft Edge */
  color: #333;
}

.m-b-0 {
  margin-bottom: 0;
}

/* ==========================================================================
     #CARD
     ========================================================================== */
.card-7 {
  background: transparent;
  position: relative;
}

.card-7 .card-body {
  padding: 0 20px;
}
.display-flex {
  display: flex;
  justify-content: center;
}
.table-container {
  display: flex;
  justify-content: center;
}
.styled-table {
  border-collapse: collapse;
  margin: 25px 0;
  font-size: 0.9em;
  font-family: sans-serif;
  min-width: 400px;
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
}
.styled-table thead tr {
  background-color: #009879;
  color: #ffffff;
  text-align: left;
}
.styled-table th,
.styled-table td {
  padding: 12px 15px;
}
.styled-table tbody tr {
  border-bottom: 1px solid #dddddd;
}
.title-text {
  padding-top: 200px;
  padding-bottom: 70px;
  color: #009879;
  font-family: "Raleway", sans-serif;
  font-size: 45px;
  font-weight: 800;
  line-height: 72px;
  margin: 0 0 24px;
  text-align: center;
  text-transform: uppercase;
}

.styled-table tbody tr:last-of-type {
  border-bottom: 2px solid #009879;
}
.error-text {
  font-size: large;
  color: rgba(255, 0, 0, 0.815);
  text-align: center;
}
.navbar {
  display: flex;
  list-style-type: none;
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: #dddddd;
}
Link {
  float: left;
}

a {
  text-decoration: none;
  display: block;
  padding: 8px;
  color: #34997a;
}
.heading {
  font-size: 1.2rem;
  line-height: 1.5;
}
.borderCircle {
  border-radius: 9999999px;
}
img {
  max-width: 100%;
  display: block;
}
button {
  outline: none;
  background: none;
  border: none;
  font-family: inherit;
}

/* ==========================================================================
     #PAGE WRAPPER
     ========================================================================== */

body {
  font-family: "Roboto", "Arial", "Helvetica Neue", sans-serif;
  font-weight: 400;
  font-size: 14px;
  margin: unset;
  height: 100vh;
  width: 100%;
  background: #ffece0;
}

/* ==========================================================================
     #BACKGROUND
     ========================================================================== */

.bg-color-1 {
  background: #ffece0;
}

/* ==========================================================================
     #BUTTON
     ========================================================================== */
.btn-submit {
  display: inline-block;
  line-height: 80px;
  font-family: inherit;
  background: #ffe151;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  border-radius: 10px;
  text-transform: capitalize;
  color: #333;
  font-size: 18px;
  -webkit-transition: all 0.4s ease;
  -o-transition: all 0.4s ease;
  -moz-transition: all 0.4s ease;
  transition: all 0.4s ease;
  padding: 0 30px;
  -webkit-box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
  -moz-box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
  box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
}

.btn-submit:hover {
  background: #ebcd3d;
}

/* ==========================================================================
     #FORM
     ========================================================================== */
input,
textarea {
  outline: none;
  margin: 0;
  border: none;
  -webkit-box-shadow: none;
  -moz-box-shadow: none;
  box-shadow: none;
  width: 100%;
  font-size: 18px;
  font-family: inherit;
}

input:disabled {
  cursor: pointer;
  background: transparent;
}

button {
  cursor: pointer;
}

.input-group {
  position: relative;
  margin-bottom: 23px;
  -webkit-box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
  -moz-box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
  box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
  padding: 16px 20px;
  padding-bottom: 10px;
  background: #fff;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  border-radius: 10px;
  float: left;
  margin-right: 10px;
}

.form::after {
  content: "";
  clear: both;
  display: table;
}

.label {
  font-size: 12px;
  color: #999;
  text-transform: uppercase;
  display: block;
  font-weight: 700;
}

.input--large {
  width: 300px;
}

.input--medium {
  width: 190px;
}

@media (max-width: 767px) {
  .input--large,
  .input--medium {
    width: 100%;
  }
}

.input--style-1 {
  font-size: 18px;
  padding: 9px 0;
  color: #666;
  font-family: "Roboto", "Arial", "Helvetica Neue", sans-serif;
}

.input--style-1::-webkit-input-placeholder {
  /* WebKit, Blink, Edge */
  color: #333;
}

.input--style-1:-moz-placeholder {
  /* Mozilla Firefox 4 to 18 */
  color: #333;
  opacity: 1;
}

.input--style-1::-moz-placeholder {
  /* Mozilla Firefox 19+ */
  color: #333;
  opacity: 1;
}

.input--style-1:-ms-input-placeholder {
  /* Internet Explorer 10-11 */
  color: #333;
}

.input--style-1:-ms-input-placeholder {
  /* Microsoft Edge */
  color: #333;
}

.m-b-0 {
  margin-bottom: 0;
}

/* ==========================================================================
     #CARD
     ========================================================================== */
.card-7 {
  background: transparent;
  position: relative;
}

.card-7 .card-body {
  padding: 0 20px;
}
.display-flex {
  display: flex;
  justify-content: center;
}
.table-container {
  display: flex;
  justify-content: center;
}
.styled-table {
  border-collapse: collapse;
  margin: 25px 0;
  font-size: 0.9em;
  font-family: sans-serif;
  min-width: 400px;
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
}
.styled-table thead tr {
  background-color: #009879;
  color: #ffffff;
  text-align: left;
}
.styled-table th,
.styled-table td {
  padding: 12px 15px;
}
.styled-table tbody tr {
  border-bottom: 1px solid #dddddd;
}
.title-text {
  padding-top: 200px;
  padding-bottom: 70px;
  color: #009879;
  font-family: "Raleway", sans-serif;
  font-size: 45px;
  font-weight: 800;
  line-height: 72px;
  margin: 0 0 24px;
  text-align: center;
  text-transform: uppercase;
}

.styled-table tbody tr:last-of-type {
  border-bottom: 2px solid #009879;
}
.error-text {
  font-size: large;
  color: rgba(255, 0, 0, 0.815);
  text-align: center;
}
.navbar {
  display: flex;
  list-style-type: none;
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: #dddddd;
}
Link {
  float: left;
}

a {
  text-decoration: none;
  display: block;
  padding: 8px;
  color: #34997a;
}
.heading {
  font-size: 1.2rem;
  line-height: 1.5;
}
.borderCircle {
  border-radius: 9999999px;
}
img {
  max-width: 100%;
  display: block;
}
button {
  outline: none;
  background: none;
  border: none;
  font-family: inherit;
}

/* ==========================================================================
     #PAGE WRAPPER
     ========================================================================== */

body {
  font-family: "Roboto", "Arial", "Helvetica Neue", sans-serif;
  font-weight: 400;
  font-size: 14px;
  margin: unset;
  height: 100vh;
  width: 100%;
  background: #ffece0;
}

/* ==========================================================================
     #BACKGROUND
     ========================================================================== */

.bg-color-1 {
  background: #ffece0;
}

/* ==========================================================================
     #BUTTON
     ========================================================================== */
.btn-submit {
  display: inline-block;
  line-height: 80px;
  font-family: inherit;
  background: #ffe151;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  border-radius: 10px;
  text-transform: capitalize;
  color: #333;
  font-size: 18px;
  -webkit-transition: all 0.4s ease;
  -o-transition: all 0.4s ease;
  -moz-transition: all 0.4s ease;
  transition: all 0.4s ease;
  padding: 0 30px;
  -webkit-box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
  -moz-box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
  box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
}

.btn-submit:hover {
  background: #ebcd3d;
}

/* ==========================================================================
     #FORM
     ========================================================================== */
input,
textarea {
  outline: none;
  margin: 0;
  border: none;
  -webkit-box-shadow: none;
  -moz-box-shadow: none;
  box-shadow: none;
  width: 100%;
  font-size: 18px;
  font-family: inherit;
}

input:disabled {
  cursor: pointer;
  background: transparent;
}

button {
  cursor: pointer;
}

.input-group {
  position: relative;
  margin-bottom: 23px;
  -webkit-box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
  -moz-box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
  box-shadow: 0px 8px 20px 0px rgba(0, 0, 0, 0.15);
  padding: 16px 20px;
  padding-bottom: 10px;
  background: #fff;
  -webkit-border-radius: 10px;
  -moz-border-radius: 10px;
  border-radius: 10px;
  float: left;
  margin-right: 10px;
}

.form::after {
  content: "";
  clear: both;
  display: table;
}

.label {
  font-size: 12px;
  color: #999;
  text-transform: uppercase;
  display: block;
  font-weight: 700;
}

.input--large {
  width: 300px;
}

.input--medium {
  width: 190px;
}

@media (max-width: 767px) {
  .input--large,
  .input--medium {
    width: 100%;
  }
}

.input--style-1 {
  font-size: 18px;
  padding: 9px 0;
  color: #666;
  font-family: "Roboto", "Arial", "Helvetica Neue", sans-serif;
}

.input--style-1::-webkit-input-placeholder {
  /* WebKit, Blink, Edge */
  color: #333;
}

.input--style-1:-moz-placeholder {
  /* Mozilla Firefox 4 to 18 */
  color: #333;
  opacity: 1;
}

.input--style-1::-moz-placeholder {
  /* Mozilla Firefox 19+ */
  color: #333;
  opacity: 1;
}

.input--style-1:-ms-input-placeholder {
  /* Internet Explorer 10-11 */
  color: #333;
}

.input--style-1:-ms-input-placeholder {
  /* Microsoft Edge */
  color: #333;
}

.m-b-0 {
  margin-bottom: 0;
}

/* ==========================================================================
     #CARD
     ========================================================================== */
.card-7 {
  background: transparent;
  position: relative;
}

.card-7 .card-body {
  padding: 0 20px;
}
.display-flex {
  display: flex;
  justify-content: center;
}
.table-container {
  display: flex;
  justify-content: center;
}
.styled-table {
  border-collapse: collapse;
  margin: 25px 0;
  font-size: 0.9em;
  font-family: sans-serif;
  min-width: 400px;
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);
}
.styled-table thead tr {
  background-color: #009879;
  color: #ffffff;
  text-align: left;
}
.styled-table th,
.styled-table td {
  padding: 12px 15px;
}
.styled-table tbody tr {
  border-bottom: 1px solid #dddddd;
}
.title-text {
  padding-top: 200px;
  padding-bottom: 70px;
  color: #009879;
  font-family: "Raleway", sans-serif;
  font-size: 45px;
  font-weight: 800;
  line-height: 72px;
  margin: 0 0 24px;
  text-align: center;
  text-transform: uppercase;
}

.styled-table tbody tr:last-of-type {
  border-bottom: 2px solid #009879;
}
.error-text {
  font-size: large;
  color: rgba(255, 0, 0, 0.815);
  text-align: center;
}
.navbar {
  display: flex;
  list-style-type: none;
  margin: 0;
  padding: 0;
  overflow: hidden;
  background-color: #dddddd;
}
Link {
  float: left;
}

a {
  text-decoration: none;
  display: block;
  padding: 8px;
  color: #34997a;
}
.heading {
  font-size: 1.2rem;
  line-height: 1.5;
}
.borderCircle {
  border-radius: 9999999px;
}
img {
  max-width: 100%;
  display: block;
}

Here is the code repository containing the code used in this blog.