Why we are (really) excited with Next.js

The web development landscape is constantly changing, with new libraries and frameworks being released every day. And in this scenario, Next.js has been gaining ample space, with more and more users worldwide.

In this article, we will discuss why Next excites the BIX Tech team and how this solution can be good for you too.

What is Next.js?

Next.js is a JavaScript framework for building server-rendered or statically generated web applications. It is built on top of Node.js, a popular runtime for building server-side applications, and is designed to make it easy to build scalable high-performance web applications.

One of the main features of Next.js is its automatic code splitting, which allows you to divide your application into smaller chunks that can be loaded on demand, improving the performance of your application. Next.js also includes a powerful routing system that turns routing into child’s play, and a component-based architecture, which allows you to divide your site’s content into reusable components.

Besides these features, it is important to notice that this framework has two forms of pre-rendering: Static Generation and Server-side Rendering. The difference is in when it generates the HTML for a page.

Using Static Generation the HTML is generated at build time and will be reused on each request. On the other hand, using Server-side Rendering, the HTML is generated on each request.

Importantly, Next.js lets you choose which pre-rendering form you’d like to use for each page. You can even create a “hybrid” Next.js app by using Static Generation for most pages and using Server-side Rendering for others, for example. This enables you to be more versatile, which enhances indexing by browser search engines and boosts the efficiency of the application.

In its documentation, Next.js recommends using Static Generation over Server-side Rendering for performance reasons. Statically generated pages can be cached by CDN (Content Delivery Network) with no extra configuration to boost performance. However, in some cases, Server-side Rendering might be the only option you will have.

You can also use Client-side data fetching along with Static Generation or Server-side Rendering. That means some parts of a page can be rendered entirely by client-side JavaScript. As always, we recommend you take a look at Next.js docs. You can find them here.

SSR versus SSG

In Next.js, SSR (Server-Side Rendering) and SSG (Static Site Generation) are two different ways of generating the content of your pages.

SSR (Server-Side Rendering) is the technique used to render the content of a page on the Server side and send it to the Client already rendered. This can be useful for improving the performance of your application, as it allows the content to be loaded faster and helps a lot with your SEO (Search Engine Optimization). However, server-side rendering can be slower and require more server resources.

SSG (Static Site Generation) is a technique used to generate the pages of your site as static files, which are delivered to the client without needing to be dynamically generated by the server. This can be useful for sites with static content or for speeding up page load times, as static files can be served directly by the file server. However, SSG is not suitable for sites with dynamic content that changes frequently, as the static files need to be regenerated each time the content changes.

In general, you can choose to utilize SSR or SSG depending on the needs of your application. If you have a site with mostly static content and want to optimize for performance and fast load times for your pages, SSG may be the better fit. But, if you have a site with dynamic content that needs to be customized for each user, SSR is your go-to option.

Why Server-Side Rendering?

The most important reason that made us adopt Next.js in some of our clients and products is certainly the SSR feature. Server-side Rendering, also referred to as Dynamic Rendering, means the HTML is generated on each request. 

When you use React without something like Next.js what happens is that the entire bundle gets delivered to the user every time they come to the website. For really small projects this might not be a big issue, but when your application starts to get larger it will slow down a lot, due to the big bundle being loaded alongside JavaScript.

By utilizing Server-side Rendering, when the user visits your web page, that individual page is going to get pre-built on a server and then get shown to the user.

Although Static Site generation is considered simpler and faster, Server Side Rendering has become a widespread technique to enhance the performance and SEO of web applications, making Next.js a powerful React framework. If we take blogs as an example, this kind of business usually is centered on selling advertisements, which makes good SEO performance a top priority. Next.js also lets us decide to easily pick between using SSR or SSG for each page of our application.

To use SSR we have to use the function getServerSideProps()
				
					function Page({ data }) {
// Render some content here
return(
		<section>
			<span>{data}</span>
		</section>
	)
}

// This will be called on every request
export async function getServerSideProps() {
	//Fetching data from external API
	const res = await fetch(`https: //.../data`)
	const data = await res.json()

	// Passing data to the page using props
return { props: { data } }
}

export default Page
				
			

The sitemap for the router and SSR

				
					import React from "react";
import * as fs from "fs";

const Sitemap = () => {
	return null;
};

export const getServerSideProps = async ({ res }) => {
	const BASE_URL = "https://base.url.com";

	const staticPathsCards = fs
		.readdirSync("pages/cards/")
		.filter((staticPage) => {
			return ![
				"api",
				"product",
				"_app.js",
				"_document.js",
				"404.js",
				"sitemap.xml.js",
			].includes(staticPage);
		})
		.map((staticPagePath) => {
			return `${BASE_URL}/cards/${staticPagePath}`;
		});

	const staticPathsBlog = fs
		.readdirSync("pages/blog/")
		.filter((staticPage) => {
			return ![
				"api",
				"product",
				"_app.js",
				"_document.js",
				"404.js",
				"sitemap.xml.js",
			].includes(staticPage);
		})
		.map((staticPagePath) => {
			return `${BASE_URL}/blog/${staticPagePath}`;
		});

	const allPaths = [...staticPathsBlog, ...staticPathsCards];

	const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
    <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
      ${allPaths
			.map((url) => {
				return `
            <url>
              <loc>${url}</loc>
              <lastmod>${new Date().toISOString()}</lastmod>
              <changefreq>monthly</changefreq>
              <priority>1.0</priority>
            </url>
          `;
			})
			.join("")}
    </urlset>
  `;

	res.setHeader("Content-Type", "text/xml");
	res.write(sitemap);
	res.end();

	return {
		props: {},
	};
};

export default Sitemap;
				
			

How does the construction of pages and routes work?

Before adopting Next.js, we were encouraged to have the Pages of our application in one folder, and separately create the Routes in another folder. In the example below using only React, we created every page inside the Pages folder and every route inside the Routes folder. As the number of pages increased, the number of routes that needed to be created increased as well. 

We can also see the definition of these routes in the code below.

				
					src
├── assets
├── components
├── hooks
├── pages
├── Routes
├── styles
├── App.jsx
├── index.jsx
└── sitemap-generator.js

// Defining Routes using React
export default function PublicRoutes() {
	return (
		<Router>
			<Wrapper>
				<Routes>
					<Route
						exact
						path="/home"
						element={<Home />}
					/>
					<Route
						exact
						path="/posts"
						element={<Posts />}
					/>
			</Wrapper>
		</Router>
	);
}
				
			

Now using the Next.js File Routing system, when a file is added to the pages directory, it’s automatically available as a route. The router also supports nested files. We can create a nested folder structure, and files will automatically be routed in the same way still.

				
					src
├── components
├── hooks
├── pages
│   └── blog
│		└── cards
│		└── _app.js
│   └── sitemap.xml.jsx
└── styles
				
			

In this example, we have a route pages/blog/post1.jsx which in the URL will be blog/post1.

Using query parameters in the URL

In Next.js, URL parameters are used to pass information from the client to the server. For example, a URL parameter can be used to identify a specific post in an online blog or to filter a list of search results.

To use a URL parameter in a Next.js page, you can use the query object that is passed to the page’s render function. For example, if your page’s URL is /posts/:slug, you can access the value of the slug parameter like this:

				
					import { GetStaticProps } from 'next';

const PostPage: React.FC<{ slug: string }> = ({ slug }) => {
  return (
    <div>
      <h1>Post Page</h1>
      <p>Slug: {slug}</p>
    </div>
  );
};

export const getStaticProps: GetStaticProps = async ({ params }) => {
  // params.slug contains the value of the slug parameter
  const post = await fetchPost(params.slug);
  return { props: { post } };
};

export default PostPage;
				
			

To pass a URL parameter as a prop to a child component, you can use the Next.js Link component and pass the parameter as part of the as object:

				
					import Link from 'next/link';

const PostsList: React.FC<{ posts: post[] }> = ({ posts }) => {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          
            <a>{post.name}</a>
          </Link>
        </li>
      ))}
    </ul>
  );
};
				
			

The Future with Next.js 13

The recently launched Next.js 13 brought several new features and improvements. One of those improvements that stand out is the native support for ECMAScript modules, which allows you to use import and export in your applications without needing a transpiler like Babel. 

Another new feature is the rework of the app directory, which now allows you to split your app into independent modules, each with its own set of pages, routes, styles, and shared components. This will help keep your app organized and able to scale as it grows. 

In addition, Next.js 13 includes a new type-checking feature that allows you to verify the types of your variables and functions using TypeScript or Flow. This can help prevent runtime errors and increase the reliability of your app. Next.js 13 also includes various performance improvements, including startup and response time optimizations, as well as a new page caching system that can speed up page load times. Let’s take a closer at some of these improvements.

New app directory (beta)

As we said above, the new app directory allows you to split your app into independent modules, each with its own set of pages, routes, styles, and shared components, which poses Next.js 13 as a great solution for creating MVPs that scale with much less pain than the other solutions available.

To use the app directory, simply create a new directory called app in the root of your app and place your app’s files inside it. For example, you can create a directory structure like this:

				
					app
├── pages
│   ├── index.tsx
│   ├── about.tsx
│   └── contact.tsx
├── components
│   ├── header.tsx
│   └── footer.tsx
├── styles
│   └── global.css
├── lib
│   └── api.ts
├── public
│   └── images
│       └── logo.png
├── next-env.d.ts
└── tsconfig.json
				
			

Once you’ve created the directory structure, you can access your app’s files using the path relative to the app directory. For example, to import the header.tsx file in your index.tsx file, you can simply utilize the:

				
					import Header from '../components/header';
				
			

Server Components

One of the added features that excite us the most about the future is the support for the new React Server Components. In short, this will give developers the ability to fully transition between using Client or Server for what they are better for. 

It was surely a no-brainer for Vercel’s Next.js team to prioritize this since it will only expand the niche of server-side access, which Next.js is already known for. 

What Vercel’s team did was that now when a route is loaded, the Next.js and React runtime will be loaded together. This load can be cacheable so the runtime will not increase even if your application grows. Also, that runtime load is asynchronous, which enables the server’s HTML to progressively update the client.

To speed up adoption, by default now every component inside the app directory is a React Server Component. This allows developers to seamlessly adopt the new Server Components with little to no friction.

It is important to notice that although the developers had their experience improved with this feature, users will also get to see that in action: if we combine Server Components and nested layouts in Next.js, the pages that don’t require data will be rendered immediately and show a loading state when data is getting fetched. This improves UX a lot by letting users interact with the page right away, instead of having to wait until everything is loaded.

Vercel’s example for the combination of Server Components and nested layouts.

Turbopack

Next.js 13 includes Turbopack, which is the new Rust-based successor to Webpack. Its predecessor has been downloaded over 3 billion times and while it’s been an integral part of building today’s web, we are close to the ceiling in terms of JavaScript-based tooling performance.

One of the main benefits of Turbopack is that it allows you to build your application faster. It does this by leveraging the power of modern JavaScript engines to execute tasks in parallel, which can significantly reduce build times. This is especially beneficial for large projects with a large number of dependencies, as the build process can often take a long time.

Another benefit of Turbopack is that it helps to reduce the size of your built application, which can improve the overall performance of your application. It does this by using advanced tree-shaking techniques to remove unused code from your application, resulting in a smaller bundle size.

In addition to these performance improvements, Turbopack also introduces several other features that can make building and maintaining a Next.js application easier. For example, it includes support for JSX in TypeScript, which allows you to use JSX syntax in your TypeScript code. It also includes a new feature called “zero-config TypeScript”, which allows you to use TypeScript without needing to configure it in your project. Overall, Turbopack is an exciting new feature in Next.js 13 that aims to improve the performance and build process of Next.js applications. It is likely to be welcomed by Next.js developers looking to improve the performance and maintainability of their projects.

Using the Turbopack alpha with Next.js 13 results in:

– 700x faster updates than Webpack

– 10x faster updates than Vite

– 4x faster cold starts than Webpack

Turbopack startup time comparison.

Concluding the ideas

There are several reasons why developers should be excited about the future of Next.js, but four main points can be highlighted:

  1. Improved performance: Next.js is known for its focus on performance, and the introduction of features like Turbopack in Next.js 13 demonstrates the team’s continued commitment to improving the performance of Next.js applications.
  2. Enhanced developer experience: Next.js has a strong focus on the developer experience, and the framework has some features that make it easier to build and maintain applications. For example, it includes support for automatic code splitting, which can help to improve the performance of your application by only loading the code that is needed for a particular page.
  3. Wide adoption: Next.js has gained a large and growing community of users and is used by many well-known companies and organizations. This means that there is a wealth of resources and support available for developers using the framework.
  4. Serverless support: Next.js has built-in support for serverless deployment, which allows you to deploy your application to a serverless platform like AWS Lambda. This can be a cost-effective and scalable way to host your application, and the support for serverless deployment in Next.js makes it easy to get started.

Overall, the future of Next.js looks promising, with a strong focus on performance, developer experience, and support for modern deployment architectures, which makes it a reasonable bet to place some of our chips on, with some of our most prolific developers doubling down on learning and shipping things using Next.js.

Want to keep talking about Next.js? Click here and schedule a conversation with an expert on this subject.

If you wish to contact the authors of this material, you can connect with Lucas Martins, Matheus Lenzi, Pedro Vieira, and Thiago Bauer on LinkedIn or forward an email to the addresses [email protected][email protected], [email protected], and  [email protected].

Don't miss any of our content

Sign up for our BIX News

Our Social Media

Most Popular