Complete Next.js Tutorial: Mastering Full Stack Development

Spread the love

Introduction to Next.js Tutorial

What is Next.js?
Next.js is a React-based framework that is perfect for building modern web applications, making it an essential subject for any comprehensive next.js tutorial. It combines the best parts of React, such as component-based architecture, with features crucial for production applications like server-side rendering, static site generation, and API routes. This makes Next.js a powerful tool for developers aiming to create fast, scalable, and SEO-friendly websites.

So, let’s begin with Next.js tutorial.

Why choose Next.js?
Choosing Next.js for your web projects simplifies the process of building complex applications. This next.js tutorial will show how Next.js handles various configurations out-of-the-box, including routing, page pre-rendering, and API services, which can greatly enhance productivity and the performance of your apps. Additionally, Next.js is backed by Vercel, ensuring it is always up-to-date and supported by a robust community.

Also Read: MOJO Lang Tutorial: Beginner’s Guide to Mastering AI Programming

Next.js Installation

So, in this Next.js Tutorial let’s begin with its installation.

Step-by-step Setup Instructions:

  1. Install Node.js:
    Ensure that Node.js is installed on your machine. You can download it from nodejs.org.
  2. Creating a Next.js App:
    Open your terminal and run the following command to create a new Next.js app, a critical first step in this next js tutorial:
npx create-next-app my-next-app

Replace my-next-app with the name you prefer for your project.

3. Navigate into your project directory:

cd my-next-app

4. Start the development server:

npm run dev
  1. This command runs your application on http://localhost:3000. Open this URL in your browser to see your Next.js application running as part of this next js tutorial.

Starting the Next.js Server

Following the steps in this next.js tutorial, use the command npm run dev to start the Next.js server and view your project. This starts the development server on localhost with hot reloading, meaning any modifications you make will immediately be visible on your browser, simplifying development and testing.

Basic Concepts: Pages and Routing

Creating Pages:

In this section of the next.js tutorial, we’ll explore how each page in Next.js is associated with a route based on its file name. For example, creating a file named about.js in the pages directory automatically maps it to the /about route.

Here’s a simple example of the about.js page:

// pages/about.js
import React from 'react';

const About = () => {
    return (
        <div>
            <h1>About Us</h1>
            <p>This is the about page of our Next.js application.</p>
        </div>
    );
}

export default About;

Linking Between Pages:

In this part of the next.js tutorial, you’ll learn how to use the Link component from next/link to navigate between pages. Here’s how to link to the About page from the homepage:

// pages/index.js
import Link from 'next/link';

const Home = () => {
    return (
        <div>
            <h1>Welcome to Our Website</h1>
            <p><Link href="/about"><a>About Us</a></Link></p>
        </div>
    );
}

export default Home;

This section of Next.js tutorial concludes the basic setup and navigation features within a Next.js application. Next, we will move on to styling and asset management in this next.js tutorial.

Styling in Next.js

How to Add Stylesheet in Next.js

In this next.js tutorial, you’ll learn how to add CSS stylesheets to your Next.js project. Next.js supports CSS out of the box, which you can apply to your components or pages directly.

Adding a Global Stylesheet:
To add a global stylesheet, follow these steps:

  1. Create a CSS file:
    Create a file named styles.css in the public directory.
  2. Link the CSS file in your application:
    Import the CSS file into your _app.js file to apply global styles.
// pages/_app.js
import '../public/styles.css';

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp;

3. Adding styles:

Add your CSS rules in the styles.css file. These styles will be available throughout your application.

How to Import SASS in Next.js

Continuing with our next.js tutorial, if you prefer using SASS/SCSS for more complex styling solutions, Next.js has built-in support for it as well. To import SCSS in Next.js, simply create a .scss file and import it into your component or page file using the standard import syntax.

Setting up SASS:

  1. Install SASS:
    Run the following command in your project directory:
npm install --save sass

2. Use SASS:

Replace or rename your CSS files with .scss or .sass extensions and update the imports accordingly.

Install & Setup Tailwind CSS with Next.js

Tailwind CSS is a utility-first CSS framework that can be integrated easily with Next.js for rapid UI development. Here’s how to install and setup Tailwind CSS with Next.js:

Also Read: Import Export Excel and CSV File using LARAVEL 10

  1. Install Tailwind CSS:
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

2. Configure Tailwind:

Edit tailwind.config.js to enable purge and add any custom configurations. Update your CSS file to include Tailwind’s directives:

/* styles/global.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

3. Import the CSS file:

Import the Tailwind CSS file into your _app.js file: Here’s a guide to import External CSS Files in NEXT.js

// pages/_app.js
import '../styles/global.css';

function MyApp({ Component, pageProps }) {
  return <Component {...pageProps} />
}

export default MyApp;

How to Add Layout in Next.js

A common practice in web development is using layouts to maintain a consistent structure across different pages. In this next.js tutorial, let’s define how to create layout in Next.js that includes a header and footer reused on multiple pages.

Creating a Layout Component:

  1. Create a Layout Component:
    Create a new file named Layout.js in the components folder.
// components/Layout.js
import React from 'react';

const Layout = ({ children }) => (
  <div>
    <header>Header Content Here</header>
    {children}
    <footer>Footer Content Here</footer>
  </div>
);

export default Layout;

2. Use the Layout in Pages:

Import and wrap your page content with the Layout component in your page files:

// pages/index.js
import Layout from '../components/Layout';

const Home = () => {
  return (
    <Layout>
      <h1>Welcome to Our Website</h1>
      <p>This is the homepage of our Next.js application.</p>
    </Layout>
  );
}

export default Home;

This section of our next.js tutorial covers the basics of styling and layout management in Next.js, essential for creating visually appealing and structurally consistent applications. In the next section, we’ll delve into image optimization and managing static assets.

Next.js Image Optimization

In this next.js tutorial, we will explore how to optimize images in Next.js using the built-in Image component provided by the framework. This component automatically optimizes images on-demand, ensuring faster page loads and efficient image sizing.

Using the Next.js Image Component:

  1. Import the Image Component:
    Open your file where you want to use an image, and import the Image component from next/image.
// pages/index.js
import Image from 'next/image';

2. Add an Image with Optimization:

Use the Image component instead of the standard HTML <img> tag. Specify the src, width, height, and optionally, layout properties.

// pages/index.js
const Home = () => {
  return (
    <div>
      <h1>Welcome to Our Website</h1>
      <Image src="/path/to/image.jpg" width={500} height={300} alt="Description"/>
    </div>
  );
}

export default Home;

This setup enables Next.js to automatically optimize and resize images as needed, dramatically improving your application’s load time.

How to Add Custom Local Fonts in Next.js

So, next in this Next.js tutorial is adding local fonts in Next.js.

Adding custom fonts is another way to enhance the visual aesthetics of your Next.js project. Here’s how to include local fonts within your next.js tutorial:

  1. Add Font Files to Your Project:
    Place your font files into the public directory.
  2. Include Fonts in CSS:
    Define @font-face in your CSS or SASS files to specify the fonts.
/* styles/global.css */
@font-face {
  font-family: 'YourCustomFont';
  src: url('/fonts/YourCustomFont.woff2') format('woff2'),
       url('/fonts/YourCustomFont.woff') format('woff');
  font-weight: normal;
  font-style: normal;
}

body {
  font-family: 'YourCustomFont', sans-serif;
}

3. Using the Font in Your Application:

Once defined, use the custom font throughout your application by referring to it in your CSS classes or styled components.

Different Forms of Pre-rendering in NextJS

Pre-rendering is a significant feature of Next.js that can enhance SEO and performance. This next.js tutorial will explain the different forms of pre-rendering in NextJS:

  1. Static Generation (getStaticProps):
    Pages are generated at build time and reused on each request.
// pages/products.js
import { getStaticProps } from 'next';

const Products = ({ products }) => {
  return (
    <div>
      <h1>Our Products</h1>
      {products.map(product => (
        <div key={product.id}>{product.name}</div>
      ))}
    </div>
  );
};

export const getStaticProps = async () => {
  const res = await fetch('https://api.example.com/products');
  const products = await res.json();

  return {
    props: {
      products
    }
  };
};

export default Products;

2. Server-side Rendering (getServerSideProps):
Pages are rendered on each request, useful for highly dynamic content.

// pages/news.js
import { getServerSideProps } from 'next';

const News = ({ articles }) => {
  return (
    <div>
      <h1>Latest News</h1>
      {articles.map(article => (
        <div key={article.id}>{article.title}</div>
      ))}
    </div>
  );
};

export const getServerSideProps = async () => {
  const res = await fetch('https://api.example.com/news');
  the articles = await res.json();

  return {
    props: {
      articles
    }
  };
};

export default News;

These sections provide a foundational understanding of how Next.js handles images, fonts, and pre-rendering. Each is crucial for building fast and SEO-friendly web applications.

Next.js Data Fetching Methods

So, net in this Next.js tutorial is data fetching methods in Next.js.

Static Generation with getStaticProps

Static Generation is the pre-rendering method where the HTML is generated at build time. This method is perfect for pages that can be pre-rendered and reused on each request, such as blog posts or e-commerce product listings.

Example of getStaticProps:

// pages/posts.js
import { getStaticProps } from 'next';

const Posts = ({ posts }) => {
  return (
    <div>
      <h1>Featured Posts</h1>
      {posts.map(post => (
        <div key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.body}</p>
        </div>
      ))}
    </div>
  );
};

export const getStaticProps = async () => {
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();

  return {
    props: {
      posts
    }
  };
};

export default Posts;

This function getStaticProps runs at build time in production, and inside it, you can fetch external data and send it as props to the page.

Server-side Rendering with getServerSideProps

Server-side Rendering (SSR) is a method where each page is generated on-demand per request, which is useful for content that changes often and is user-specific.

Example of getServerSideProps:

// pages/news.js
import { getServerSideProps } from 'next';

const News = ({ articles }) => {
  return (
    <div>
      <h1>Latest News</h1>
      {articles.map(article => (
        <div key={article.id}>
          <h3>{article.title}</h3>
          <p>{article.content}</p>
        </div>
      ))}
    </div>
  );
};

export const getServerSideProps = async () => {
  const res = await fetch('https://api.example.com/news');
  const articles = await res.json();

  return {
    props: {
      articles
    }
  };
};

export default News;

This method is ideal for handling data that changes often, such as user-specific data or real-time information like news articles.

Client-side Data Fetching

Client-side data fetching is used when you do not need to pre-render the data. This method is ideal for user-specific pages (like dashboards), private user data, or when the data does not need to be rendered in the server-rendered markup.

Example with React Hooks:

// pages/dashboard.js
import { useEffect, useState } from 'react';

const Dashboard = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch('https://api.example.com/user-data')
      .then(response => response.json())
      .then(data => setData(data))
  }, []);

  return (
    <div>
      <h1>Your Dashboard</h1>
      {data ? <p>Welcome back, {data.user}!</p> : <p>Loading...</p>}
    </div>
  );
};

export default Dashboard;

In this example of Next.js tutorial, data fetching is handled on the client-side, making it independent of the Next.js server-side rendering pipeline.

These data fetching methods provide flexible options for different scenarios in your Next.js applications, enhancing the overall experience by ensuring data is loaded efficiently and appropriately.

Next.js Dynamic Route Segments

Dynamic routes in Next.js are used to handle patterns where pages have variable paths, such as user profiles, blog post slugs, or any other parameter-based content.

Creating Dynamic Routes

To create a dynamic route, you name the page file with bracketed parameters. For example, to create a dynamic page for blog posts:

File Name: [slug].js in the pages/posts directory.

Here’s how you set it up:

// pages/posts/[slug].js
import { useRouter } from 'next/router';

const Post = () => {
  const router = useRouter();
  const { slug } = router.query; // Access route parameters

  return (
    <div>
      <h1>Post: {slug}</h1>
      <p>This is the blog post content for {slug}.</p>
    </div>
  );
};

export default Post;

This example demonstrates how you can access the slug parameter from the URL to dynamically generate the content based on the route.

Fetching Data for Dynamic Routes

To fetch data based on dynamic route parameters using getStaticPaths and getStaticProps:

Example:

// pages/posts/[slug].js
import { getStaticPaths, getStaticProps } from 'next';

const Post = ({ post }) => {
  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.content}</p>
    </div>
  );
};

export const getStaticPaths = async () => {
  const res = await fetch('https://api.example.com/posts');
  const posts = await res.json();

  const paths = posts.map(post => ({
    params: { slug: post.slug },
  }));

  return { paths, fallback: false };
};

export const getStaticProps = async ({ params }) => {
  const res = await fetch(`https://api.example.com/posts/${params.slug}`);
  const post = await res.json();

  return { props: { post } };
};

export default Post;

This code snippet demonstrates how getStaticPaths is used to generate paths for static generation based on external data, and getStaticProps fetches the data specific to each path.

Next.js Dynamic Import

So, let’s begin with dynamic import in this Next.js tutorial.

Dynamic imports in Next.js help in loading modules or components only when needed. This is particularly useful for improving load times and optimizing resource usage in larger applications.

Example of Dynamic Import:

// This component is loaded only when needed
const DynamicComponent = dynamic(() => import('../components/ExpensiveComponent'), {
  loading: () => <p>Loading...</p>,
  ssr: false,
});

const HomePage = () => {
  return (
    <div>
      <h1>Welcome to the Home Page</h1>
      <DynamicComponent />
    </div>
  );
};

export default HomePage;

By using dynamic imports, you can reduce the initial load time of your Next.js application, which enhances user experience by loading non-critical resources on demand.

Next.js API Routes

So, we’ve come to the point of API routes in this Next.js tutorial.

API routes allow you to create RESTful APIs directly within your Next.js application. This is particularly useful for handling backend logic, such as database interactions, authentication, and other server-side processes.

Creating an API Route

To set up an API route, create a file inside the pages/api directory. Each file corresponds to a route that can handle requests.

Example: Creating a simple API to fetch user data:

// pages/api/user.js
export default function handler(req, res) {
  // This API could connect to a database or any other data source
  res.status(200).json({ name: 'John Doe', age: 30 });
}

This API route can be accessed at /api/user and will return a JSON response with user data.

Handling Different Request Methods

You can also handle different HTTP methods within the same API route to perform CRUD operations:

Also Read: MOJO vs Python: An In-Depth Comparison for Developers

// pages/api/posts.js
export default function handler(req, res) {
  switch (req.method) {
    case 'GET':
      res.status(200).json({ posts: [] });
      break;
    case 'POST':
      // Code to handle POST request
      res.status(201).send('Post created');
      break;
    default:
      res.setHeader('Allow', ['GET', 'POST']);
      res.status(405).end(`Method ${req.method} Not Allowed`);
  }
}

This example shows how to handle GET and POST requests within the same route, providing different responses based on the method.

Next.js Middleware

Middleware in Next.js allows you to run code before completing a request, and it can be used for tasks like server-side redirection, custom authentication, or logging.

Example of Using Middleware

You can create middleware in Next.js by adding a file in the middleware folder or directly in your pages or API routes using the config export:

Example: Redirecting a user based on a condition:

// pages/_middleware.js
import { NextResponse } from 'next/server';

export function middleware(request) {
  const { pathname } = request.nextUrl;

  if (pathname == '/') {
    // Redirecting from the homepage to the about page
    return NextResponse.redirect('/about');
  }

  return NextResponse.next();
}

This middleware checks if the user is visiting the homepage and redirects them to the about page.

By utilizing API routes and middleware, you can greatly enhance the functionality of your Next.js applications, turning them into full-fledged web applications capable of handling complex server-side logic.

Next.js Fast Refresh

Fast Refresh is a Next.js feature that provides instant feedback on edits made to your React components. It’s a powerful tool for developers, enhancing the development experience by allowing you to see changes in real time without losing component state.

Understanding Fast Refresh:

  • Fast Refresh automatically reloads the component you edited without refreshing the whole page.
  • It retains the React state in functional components as long as hooks are not violating the Rules of Hooks.
  • In case of an error, Fast Refresh will perform a full reload to help you start again from a clean state.

Fast Refresh works out of the box in all Next.js applications, starting from version 9.4, making development quicker and more error-resilient.

Next.js Custom Server

Next.js can be used with a custom server, giving you the ability to override the default server configuration. This is useful for integrating with existing backends, handling custom routing patterns, or advanced server-side handling.

Example of Setting Up a Custom Server:
Here’s a basic example using Express:

// server.js
const express = require('express');
const next = require('next');

const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();

app.prepare().then(() => {
  const server = express();

  server.get('/a', (req, res) => {
    return app.render(req, res, '/a', req.query);
  });

  server.get('/b', (req, res) => {
    return app.render(req, res, '/b', req.query);
  });

  server.all('*', (req, res) => {
    return handle(req, res);
  });

  server.listen(3000, (err) => {
    if (err) throw err;
    console.log('> Ready on http://localhost:3000');
  });
});

This script sets up an Express server that handles specific routes differently and falls back to the default Next.js handler for others.

Next.js Environment Variables

Environment variables in Next.js allow you to insert server-side and public configuration data safely. These variables can be loaded from .env files or your deployment environment directly.

Setting Up Environment Variables:

  1. Create a .env.local file at the root of your project.
  2. Add environment-specific variables there:
DATABASE_USER=janedoe
DATABASE_PASSWORD=example

3. Use these variables in your Next.js pages or API routes:

// pages/api/db.js
export default function handler(req, res) {
  const dbUser = process.env.DATABASE_USER;
  const dbPassword = process.env.DATABASE_PASSWORD;
  // Use dbUser and dbPassword to connect to your database
  res.status(200).json({ message: 'Connected to DB successfully!' });
}

These features are key to building scalable, efficient, and secure applications with Next.js. They provide developers with the tools needed to manage development environments, customize server behavior, and securely configure applications.

How to Deploy Next.js App to Vercel

Vercel, the creators of Next.js, offer a platform that provides seamless deployment for Next.js applications. The process is straightforward and optimized for performance.

Steps to Deploy on Vercel:

  1. Push Your Code to a Git Repository:
    First, ensure your project is pushed to a Git provider such as GitHub, GitLab, or Bitbucket.
  2. Create a Vercel Account:
    Sign up at Vercel’s website.
  3. Connect Your Git Repository to Vercel:
    After signing up, connect your Vercel account to your Git repository by importing your project.
  4. Configure Your Project:
    Vercel automatically detects that it’s a Next.js app and sets up the build settings for you. If needed, you can customize the build commands and environment variables in the project settings.
  5. Deploy:
    Every push to your chosen branch (commonly main) triggers a new deployment automatically. Vercel builds, deploys, and provides you with a live URL.
  6. Domain Setup:
    You can assign a custom domain through Vercel’s dashboard, enhancing your project’s professional presence.

Adding User Authentication in Next.js using NextAuth

NextAuth.js is a popular choice for handling authentication in Next.js apps. It’s easy to integrate and supports many different authentication providers.

Setting Up NextAuth:

  1. Install NextAuth: Add NextAuth to your project:
npm install next-auth

2 Create [...nextauth].js API Route:

Set up an API route in pages/api/auth/[...nextauth].js:

import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';

export default NextAuth({
  providers: [
    Providers.Google({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
    // Add other providers as needed
  ],
  // Configure other options like database, pages for signIn, signOut, etc.
});

3. Using Session and Auth Hooks:

Utilize the useSession hook in your components to manage authentication state:

import { useSession, signIn, signOut } from 'next-auth/react';

const Component = () => {
  const { data: session } = useSession();
  if (session) {
    return <>
      <p>Welcome, {session.user.name}!</p>
      <button onClick={() => signOut()}>Sign Out</button>
    </>;
  }
  return <button onClick={() => signIn()}>Sign In</button>;
};

This setup not only secures your application but also enhances user experience by providing familiar and straightforward login mechanisms.

Feature Integration

UI Components and Widgets

In this Next.js Tutorial, now let’s delve into how to add various UI elements to your Next.js applications, enhancing interactivity and functionality.

  1. Calendar Integration:
    • Purpose:
      Allow users to pick dates for events or appointments.
    • Implementation:
// components/CalendarComponent.js
import React from 'react';
import { Calendar } from 'react-calendar';
import 'react-calendar/dist/Calendar.css'; // Import default CSS

const CalendarComponent = () => {
  const [value, onChange] = React.useState(new Date());

  return <Calendar onChange={onChange} value={value} />;
};

export default CalendarComponent;
  • Usage:
    Add this component to any page where you need date selection functionality.

2. Image Carousel:

  • Purpose:
    Display a series of images in a slide show.
  • Implementation:
    Using a simple library like react-slick to implement an image carousel.
// components/ImageCarousel.js
import React from 'react';
import Slider from 'react-slick';
import "slick-carousel/slick/slick.css"; 
import "slick-carousel/slick/slick-theme.css";

const images = ['/img1.jpg', '/img2.jpg', '/img3.jpg'];

const ImageCarousel = () => {
  const settings = {
    dots: true,
    infinite: true,
    speed: 500,
    slidesToShow: 1,
    slidesToScroll: 1
  };

  return (
    <Slider {...settings}>
      {images.map((img, index) => (
        <div key={index}>
          <img src={img} alt={`Slide ${index}`} />
        </div>
      ))}
    </Slider>
  );
};

export default ImageCarousel;
  • Usage:
    Integrate this carousel in product detail pages or as part of a gallery feature.

Functional Features

Adding more interactive and practical functionalities enhances user engagement.

  1. Digital Clock:
    • Implementation:
// components/DigitalClock.js
import React, { useState, useEffect } from 'react';

const DigitalClock = () => {
  const [time, setTime] = useState(new Date());

  useEffect(() => {
    const timer = setInterval(() => {
      setTime(new Date());
    }, 1000);

    return () => clearInterval(timer);
  }, []);

  return <div>{time.toLocaleTimeString()}</div>;
};

export default DigitalClock;
  • Usage:
    Can be used on dashboards or in utility apps where time tracking is essential.

2. Loading Quotes Feature:

  • Purpose: Display inspirational quotes while loading data.
  • Implementation:
// components/LoadingQuotes.js
import React from 'react';

const quotes = [
  "Don't cry because it's over, smile because it happened.",
  "Be yourself; everyone else is already taken.",
  "So many books, so little time."
];

const LoadingQuotes = () => {
  const randomQuote = quotes[Math.floor(Math.random() * quotes.length)];

  return <div>Loading... <em>{randomQuote}</em></div>;
};

export default LoadingQuotes;
  • Usage:
    Great for enhancing user experience during longer loading times.

Full Stack Development Integration

Incorporating backend capabilities with Next.js for a full-stack development approach.

  1. Setting Up an API Backend:
    • Using Next.js API routes to handle CRUD operations for a blog.
    • Implement secure API authentication methods using JWT tokens.
  2. Frontend Integration:
    • Fetching Data: Utilize getServerSideProps for dynamic page rendering based on API data.
    • State Management: Using React Context or Redux for managing state across the application.

This expanded coverage ensures that we comprehensively address the practical application of Next.js in building and deploying scalable, feature-rich web applications.

Also Read: Rename Local and Remote Git Branch: A Step-by-Step Guide

Conclusion and Best Practices

Wrapping up this next.js tutorial, you’ve learned how to build a full-featured Next.js application, from setting up your environment to deploying and adding authentication. Here are some best practices to keep in mind:

  • Keep Dependencies Updated:
    Regularly update your Next.js and other dependencies to leverage the latest improvements and security patches.
  • Use Environment Variables:
    Secure sensitive keys and configurations using environment variables.
  • Monitor Performance:
    Utilize tools like Vercel Analytics or Google’s Lighthouse to monitor and optimize your application’s performance.

Now that in this Next.js Tutorial we’ve covered deploying your Next.js application and adding authentication, you’re well-equipped to take your Next.js skills to the next level, building and deploying professional-grade web applications.

Hope you’ve found this Next.js Tutorial helpful.

Leave a Comment