Discover Next.js and write server-side React apps the easy way

Introduction

React has revolutionized web development in recent years with its component-based architecture and declarative approach to building user interfaces. According to the State of JS 2021 survey, React is the most popular front-end framework with 80% of respondents having used it and would use it again.

But as applications grow in size and complexity, challenges arise with things like code splitting, server-side rendering, and static site generation. This is where Next.js enters the picture.

Next.js is an opinionated, batteries-included framework for building production-ready React applications. It provides a powerful set of features on top of React that optimize performance and developer experience. Since its initial release in 2016, Next.js has seen tremendous growth and adoption. Let‘s explore what makes it so special.

What is Next.js?

Next.js is an open-source web development framework built on top of Node.js and React that enables functionality such as server-side rendering and static site generation. It is described by its creators, Vercel (formerly ZEIT), as "The React Framework for Production."

Some key features of Next.js include:

  • Automatic server rendering and code splitting
  • Static site generation with next export
  • Easy page routing based on file system
  • API routes for building backend functionality
  • Support for CSS-in-JS styling
  • Hot code reloading in development
  • Automatic TypeScript support
  • Optimized Webpack and Babel configuration
  • Built-in linting and formatting tools

Next.js abstracts away many of the common challenges and boilerplate involved in configuring a server-rendered React application. It provides sensible defaults and conventions so you can focus on writing your application code, not low-level plumbing.

To showcase the growth of Next.js, here are some interesting statistics:

  • Next.js has over 1.8 million weekly downloads on npm as of May 2023 (source)
  • The Next.js GitHub repository has over 98,000 stars and 2,400 contributors (source)
  • Vercel, the creators of Next.js, raised a $150 million Series D funding round in December 2021 (source)

Benefits of Next.js

So why would you choose Next.js over plain React? Let‘s look at some of the key benefits.

Server-Side Rendering

Server-side rendering (SSR) is the ability to render React components on the server and send the HTML to the client. This is in contrast to client-side rendering where the initial HTML is largely empty and the browser must download and evaluate JavaScript before the page becomes interactive.

SSR can provide several benefits:

  • Faster initial page loads, especially on mobile devices or slow networks
  • Improved search engine optimization (SEO) as crawlers can index fully formed HTML
  • Consistent performance across different browsers and devices

Here‘s a simple example contrasting client-side rendering with server-rendering in Next.js. With client-side rendering, the initial HTML looks like this:

<!doctype html>
<html>
  <head>
    <script src="bundle.js"></script>
  </head>
  <body>
    <div id="root"></div>
  </body>
</html>

The content is not available until the JavaScript bundle loads and React hydrates the page. In contrast, with Next.js the server renders the initial HTML which looks like:

<!doctype html>
<html>
  <head>
    <title>My App</title>
  </head>
  <body>
    <div id="root">
      <div>

        <p>Welcome to my server-rendered React app!</p>  
      </div>
    </div>
    <script src="bundle.js"></script>
  </body>
</html>

The content is available immediately while React still hydrates in the background for interactivity. This can result in a faster perceived load time.

Static Site Generation

In addition to server rendering, Next.js supports static site generation (SSG). With SSG, the HTML for every possible route is generated at build time. This means no server is needed at runtime, allowing you to deploy to static hosting services and CDNs for maximum performance and scalability.

SSG is well suited for content-heavy websites like blogs, documentation, and marketing pages. But Next.js takes it a step further by supporting hybrid architectures. You can opt to statically generate some pages while server rendering others, even fetching data at build time for truly dynamic static pages.

To statically generate a page, you export an async function called getStaticProps from the page component. Next.js will call this function at build time to fetch data and pass it as props. Here‘s an example:

function Blog({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

export async function getStaticProps() {
  const res = await fetch(‘https://.../posts‘);
  const posts = await res.json();

  return {
    props: {
      posts,
    },
  }
}

export default Blog;

During the build process, Next.js will call getStaticProps, fetch the blog posts, and generate a static /blog page with the data inlined. This makes it extremely fast to serve.

SSG is increasingly popular for its performance benefits. According to the Web Almanac, 50% of developer respondents used a static site generator in 2021, up from 36% in 2020.

API Routes

Next.js allows you to easily create API endpoints as Node.js serverless functions. Any file inside the pages/api directory is treated as an API route instead of a page.

API routes let you securely communicate with backend services or databases without exposing them to the client. They can also be used to mask third-party API calls to avoid exposing sensitive tokens or to modify the response format.

Here‘s an example API route that fetches blog posts from a database:

// pages/api/blog.js

import db from ‘lib/db‘;

export default async function handler(req, res) {
  const posts = await db.query(‘SELECT * FROM posts‘);

  res.status(200).json(posts);
}

You can then call this API from your page components like:

fetch(‘/api/blog‘)
  .then(res => res.json()) 
  .then(data => console.log(data));

API routes make it easy to build full-stack applications with Next.js without needing a separate backend server. They can even be deployed as serverless functions for automatic scaling and reduced costs.

Performance Optimizations

Next.js takes performance seriously and includes many optimizations by default. Let‘s examine a few.

Code Splitting

A major performance bottleneck in JavaScript applications is the size of the code bundle. As the bundle grows, it takes longer to download, parse, and evaluate, delaying interactivity.

Next.js solves this by automatically splitting your JavaScript into manageable chunks. Each page and its dependencies are bundled into a separate chunk that is loaded on demand as the user navigates through your application.

For example, say your application has the following pages:

pages/
  index.js
  about.js
  contact.js

Next.js will generate three separate JavaScript bundles, one for each page. When a user visits the home page, only the code for index.js and its dependencies are downloaded. The other bundles are loaded only if the user navigates to those routes. This ensures fast startup times even as your application grows.

Prefetching

In addition to code splitting, Next.js will automatically prefetch the JavaScript bundles for other pages when a page is idle. Prefetching is triggered by the <Link> component when it appears in the viewport.

For example, if the current page links to /about like:

<Link href="/about">
  <a>About</a>
</Link>

Next.js will start loading the code for /about in the background so that when the user clicks the link, the transition feels near-instant. This optimization is entirely automatic and requires no configuration.

Image Optimization

Images are often the largest assets on a web page, slowing down load times significantly. Next.js includes a built-in Image component that automatically optimizes images for the web.

The Image component will:

  • Automatically serve modern formats like WebP if the browser supports it
  • Resize and optimize images on-demand for different screen sizes
  • Defer loading offscreen images to improve the initial page load

Here‘s a basic example:

import Image from ‘next/image‘;

function AvatarImage() {
  return (
    <Image
      src="/avatar.png"
      alt="Avatar"
      width={500}
      height={500}
    />
  );
}

Next.js will automatically generate multiple versions of the image at different sizes and formats and deliver the optimal one based on the user‘s device.

These automatic optimizations help make Next.js applications fast by default. In a case study on web.dev, the popular trivia game Jeopardy! saw a 61% improvement in Lighthouse performance scores after migrating to Next.js.

The Future of Next.js

Next.js has come a long way since its initial release in 2016. Let‘s look at some recent developments and what the future may hold.

With the release of Next.js 12 in October 2021, new features like URL imports and middleware were introduced. URL imports allow you to import modules from external URLs, enabling new use cases like importing components from a design system. Middleware enables you to run code before a request is processed, useful for things like authentication, A/B testing, and advanced redirects.

Looking further ahead, the Next.js team is investing in features like React Server Components to make server rendering more efficient, and Edge Functions to run serverless code closer to the user. Automatic font optimization is also on the roadmap to further improve performance.

From the words of Next.js creator Guillermo Rauch:

We‘re investing in the future of React and the Web with features like React Server Components, Edge Functions, and more. Our goal is to make Next.js the best framework for building modern web applications.

As React itself continues to evolve with features like concurrent mode and suspense, Next.js is well positioned to grow alongside it and continue simplifying the development of server-rendered and statically-generated React applications.

Conclusion

Next.js has established itself as the go-to framework for building production-ready React applications. Its convention-based approach strikes a balance between automating common tasks and allowing flexibility for complex use cases.

Features like automatic code splitting, server-side rendering, static site generation, and API routes enable developers to build full-stack applications with ease. Performance optimizations like prefetching and image optimization make Next.js apps fast by default.

While it‘s impossible to predict the future, the continued growth and investment in Next.js signal a bright future for the framework. As the web moves towards more dynamic and personalized experiences, tools like Next.js will become increasingly essential.

If you‘re a React developer looking to build server-side rendered applications, Next.js is definitely worth adding to your toolkit. Its intuitive design and powerful features make it a joy to work with. Give it a try and see for yourself!

Similar Posts