1. javascript
  2. /frameworks
  3. /next-js

An Introduction to Next.js

Getting Started

In recent years, web applications have become increasingly complex, with more data and interactions required to meet user demands. React, a popular front-end JavaScript library has helped developers build sophisticated web applications with its component-based architecture and declarative syntax.

Next.js, a versatile framework created by Vercel, builds on and extends React's capabilities by allowing applications to render content on the server side. Consequently, the approach results in faster page load times and better search engine optimization, as well as a crisp user experience.

Preconditions for Learning Next.js

To effectively work with Next, you should have a solid understanding of React, including component-based architecture, JSX syntax, and state management. Feel free to check out our Introduction to React and as always, there's no shame in brushing up on some ES6 JavaScript syntax while you're at it.

Moreover, familiarity with server-side rendering and knowledge of Node.js is highly advised. We recommend going through their official guides, as they are thorough and clear enough, and will help you learn the ropes and understand the basic setup.

Next.js Pages

In a Next.js project, each Javascript file in the pages directory exports a React component that represents a route in the application. The file structure mirrors the actual URLs that the user will navigate to, and Next.js provides its own router to make the navigation seamless.

Next.js uses a file-based routing system to create pages. Each page is a React component that exports a default function that returns JSX. The filename of the page corresponds to the URL path. For example, a file named about.js would correspond to the path /about.

import Link from 'next/link'

export default function Home() {
  return (
    <div>
      <h1>Welcome to Next.js!</h1>
      <Link href="/about">
        <a>About Us</a>
      </Link>
    </div>
  )
}

Data Fetching Strategies

Next.js offers several rendering strategies to meet different performance, data fetching, and SEO requirements.

Server Side Rendering (SSR)

When a user requests a web page, the server generates the HTML for the page and sends it back to the browser. The browser then renders the HTML and displays the page to the user. With SSR, the user always sees a fully loaded web page with content rendered on the server-side. In Next.js, we can use the getServerSideProps() function to fetch data and perform SSR.

Static Site Generation (SSG)

Static Site Generation is a rendering strategy for sites with mostly static content that doesn't change often. With SSG, we generate HTML pages at build-time, which means that the HTML is already pre-rendered before the user requests it. This approach can lead to faster page loads and better SEO, as the content is available for search engines to crawl. In Next.js, we can use the getStaticProps() function to perform SSG.

Client Side Rendering (CSR)

Client Side Rendering is a strategy where the browser loads the web page shell, fetches the JavaScript code and renders the content on the client-side. CSR is great for apps that rely on real-time data and user interactions, as it allows for fast and seamless updates without requiring a full page refresh.

Incremental Static Regeneration (ISR)

Incremental Static Regeneration is a hybrid rendering strategy that combines the benefits of both SSG and SSR. With ISR, we can re-generate the HTML of a page on-demand, at a specified time interval, or when new data becomes available. For instance, we can use the getStaticProps() function with a revalidate option to perform ISR.

These rendering strategies offer different trade-offs and are suitable for different use cases. Next.js makes it easy to switch between them and pick the best one for your app based on your requirements.

Server-side Rendering with Next.js pages

As mentioned in the opening, the main benefit of Next as a framework is its built-in server-side rendering capabilities. When a user requests a page, Next.js renders the page on the server and returns the fully rendered HTML to the client With that, the page loading times are much faster and the application is way more SEO-friendly.

function About({ data }) {
  return <p>{data}</p>;
}

export async function getServerSideProps() {
  const res = await fetch("https://api.example.com/about");
  const data = await res.text();
  return {
    props: {
      data,
    },
  };
}

export default About;

As we can see, the About page fetches data from an external API using the getServerSideProps function. The function runs on the server and returns the fetched data as props to the component, which can then render it to the page.

The Built-in CSS Support

Next.js provides built-in support for styling your applications with CSS. We have the option of using standard CSS files or CSS modules, allowing us to scope the styles to specific components. Crucially, Next automatically optimizes the CSS by removing unused styles and generating critical CSS for the initial page load.

Using CSS modules

We can write modular and reusable CSS in our Next.js application with CSS modules as they give us the option of scoping CSS styles to a specific component and help prevent any unintended style collisions.

import styles from './Button.module.css';

const Button = () => {
  return (
    <button className={styles.button}>
      Click me
    </button>
  );
};

export default Button;

We can deduce that the CSS module is defined in the Button.module.css file and imported into the Button component. Moreover, the className of the button is set to styles.button, referring to the button class defined in the CSS module. This ensures that the CSS styles are only applied to the Button component and not to any other components in the application.

Using Global Styles

In Next.js, we also have the possibility of using global styles by importing a global CSS file into our pages.

import '../styles/global.css';

const MyApp = ({ Component, pageProps }) => {
  return <Component {...pageProps} />;
};

export default MyApp;

Here, the global.css file is imported into the MyApp component ensuring that the global styles are applied to all pages in the application.

API Routing in Next.js

Next.js also offers an API routing feature that enables us to create API endpoints as serverless functions. These API endpoints can be used to fetch and update data from a server-side database, or to create custom server-side functionality that can be called from the client-side. We should note that this could be particularly useful for creating lightweight APIs that can be easily integrated into front-end applications.

// pages/api/hello.js

export default function handler(req, res) {
  res.status(200).json({ message: 'Hello, World!' });
}

In this specific example, a basic API route is created in the pages/api/hello.js file. The handler function is responsible for handling the incoming request and returning a JSON response with the message "Hello, World!".

Passing Query Parameters to API routes

We can also pass query parameters to API routes in Next.js by accessing the req.query object.

// pages/api/users/[id].js

export default function handler(req, res) {
  const { id } = req.query;

  res.status(200).json({ userId: id });
}

An API route is created that accepts a query parameter id in the URL. The parameter is then accessed from the req.query object and returned as a JSON response.

The Power of Built-in Optimization

Next.js provides built-in optimizations that make the UX and core vitals of apps cutting-edge and competitive. Some of the optimizations include:

  • Automatic code splitting, where Next.js splits your code into smaller chunks to improve performance and reduce initial load time.

  • Automatic optimization of images for different screen sizes and device types to improve performance and reduce data usage.

  • Automatic preloading, where Next.js preloads pages and assets to improve performance and reduce the time it takes to navigate between pages.

Let's take a look at how automatic preloading works in Next.js:

import Link from 'next/link';

const HomePage = () => {
  return (
    <div>
      <h1>Welcome to my app!</h1>
      <p>This is the homepage.</p>
      <Link href="/about" prefetch>
        <a>About Us</a>
      </Link>
    </div>
  );
};

export default HomePage;

In this case, the Link component preloads the /about page when the user hovers over the link, reducing the total time it takes to load it.

Final Thoughts

With its ease of use, performance benefits, and built-in optimizations, Next.js is a great choice for building modern web applications. Whether you are building a small blog or a large e-commerce site, Next.js provides the tools and features you need to deliver a fast, engaging, and user-friendly web experience.

However, this is just a brief overview of some of the features as Next.js offers a variety of data fetching strategies and additional tooling. To fully understand the framework, we advise you to dive deeper into the documentation and explore the guides that will potentially inspire you to set up your own project.

Useful Resources