Future Web with Production Ready Next.js Applications

Ainomugisha Solomon
Stackademic
Published in
8 min readOct 16, 2023

--

Next.js is a flexible React framework that provides building blocks to create fast web applications. It handles the tooling and configuration needed for React and provides additional structure, features, and optimizations for your application nextjs.org.

Next JS was created by Vercel, providing React-based web applications with server-side rendering and static website generation en.wikipedia.org.

Next.js extends the functionality of traditional React apps, which can only render their content in the client-side browser, to include applications rendered on the server-side en.wikipedia.org.

Here are the key features of the Next JS UI framework

Image Component and Optimisation

The image component next/image is an extension of the HTML <img> element designed to satisfy modern web development needs. It consists of an array of built-in optimizations such as visual stability, asset flexibility, faster page loads, performance enhancement to help you achieve desired Core Web Vitals.

Internationalised Routing

From v10.0.0, Next JS has prioritized built-in support for internationalized routing. You just need to assign a list of locales, domain-based locales, and the default locale. Later, the Next JavaScript framework will handle all the routing requirements automatically.

Dedicated Analytics

Get access to actual visitor data to analyze real experience scores. The framework helps you gather web vitals from real devices that your visitors use. You gain actionable insights into the source code and pages that are contributing to the score. This helps in quick pivoting and iteration to improve overall performance and workflow.

Zero-configuration

Next JS facilitates automatic bundling and compilation to create new apps via create-next-app. This feature is optimized for production from the start to support Node JS 12.22.0 or later versions. It also offers complete assistance for platforms like macOS, Linux, and Windows

Hybrid Data Fetching

Depending on your app’s use case, you can render your content in several ways with Next JS Server Side Rendering and Server Side Generation (SSG). You can also update or create content for a particular use case at runtime with the help of Incremental Static Regeneration (ISR).

Incremental Static Regeneration(ISR)

You can develop or modify your static pages with this framework. It enables your NextJS developers to use static generation page-by-page with Incremental Static Regeneration and avoid the complexities of rebuilding the site. With this, you can achieve high scalability while retaining the advantages of being static.

TypeScript Support

The Next JS framework offers automatic TypeScript configuration and compilation for APIs, Pages, and more. This built-in support feature gives an integrated TypeScript experience to start a new project from scratch or make changes to an existing one. You get enough room to exploit custom compiler options for the same.

Fast Refresh

Any changes and edits made to your React components can be viewed instantly with Fast Refresh. It is a default feature for all Next JS apps on 9.4 or the latest versions. With this, you get a live-editing experience and instantaneous feedback to keep your app up and running in accordance with the component state.

File-system Routing

The framework is driven by a file-system-based router that eliminates the need to configure any URL for routing purposes. This router promotes automatic routing around pages. It converts each component in the pages’ directory into a route with an added provision to define nested, index, and dynamic routes.

API Routes

API routes is an important Next JS feature that helps you develop API seamlessly. Basically, it helps to create API endpoints that offer robust backend functionality. It focuses on the server side only bundles without making any changes or increasing the size of your client side bundling.

Built-in CSS Support

With this feature, you get dedicated Syntactically Awesome Style Sheets (Sass) support. You can import CSS files from a JavaScript file as Next JS extends the method of import beyond JavaScript. You can build component-level styles with the CSS modules using the file naming convention — [name].module.css.

Code-splitting and Bundling

Next JS has the potential to render pages with the needed libraries as per the project requirements. It concentrates on the creation of multiple resources rather than a large solo JavaScript file. As such, whenever a page loads, you can view the required JavaScript page with it.

Before taking your Next.js application to production, here are some recommendations to ensure the best user experience.

Use Caching wherever possible.

Caching improves response times and reduces the number of requests to external services. Next.js automatically adds caching headers to immutable assets served from /_next/static including JavaScript, CSS, static images, and other media.

Cache-Control: public, max-age=31536000, immutable

Cache-Control headers set in next.config.js will be overwritten in production to ensure that static assets can be cached effectively. If you need to revalidate the cache of a page that has been statically generated, you can do so by setting revalidate in the page's getStaticProps function. If you're using next/image, you can configure the minimumCacheTTL for the default Image Optimization loader.

Cache-Control: no-cache, no-store, max-age=0, must-revalidate

You can also use caching headers inside getServerSideProps and API Routes for dynamic responses. For example, using stale-while-revalidate.

// This repeated within the next 10 seconds, the previously// cached value will still be fresh. If the request is repeated before 59 seconds,// the cached value will be stale but still render (stale-while-revalidate=59).//// In the background, a revalidation request will be made to populate the cache// with a fresh value. If you refresh the page, you will see the new value.export async function getServerSideProps({ req, res }) {  res.setHeader(    'Cache-Control',    'public, s-maxage=10, stale-while-revalidate=59'  )   return {    props: {},  

By default, Cache-Control headers will be set differently depending on how your page fetches data.

If the page uses getServerSideProps or getInitialProps, it will use the default Cache-Control header set by next start in order to prevent accidental caching of responses that cannot be cached. If you want a different cache behavior while using getServerSideProps, use res.setHeader('Cache-Control', 'value_you_prefer') inside of the function as shown above.

If the page is using getStaticProps, it will have a Cache-Control header of s-maxage=REVALIDATE_SECONDS, stale-while-revalidate, or if revalidate is not used, s-maxage=31536000, stale-while-revalidate to cache for the maximum age possible.

Ensure database and backend are deployed in the same region

Hosting your database and backend in the same region can have several benefits:

  1. Reduced Latency: When your application’s users are in the same geographical region as your backend and database, the time it takes for data to travel between the user and the server is significantly reduced. This can result in a smoother and more responsive user experience
  2. Improved Performance: Having your backend and database in the same region can lead to improved performance. This is because data doesn’t have to travel far to get from the frontend to the backend, and vice versa. This can result in faster response times and a more efficient application
  3. Cost Efficiency: If your application serves users in a specific region, hosting your backend and database in the same region can be cost-effective. This is because you’re reducing data transfer costs that can occur when data has to travel between different regions
  4. Simplified Architecture: Hosting your backend and database in the same region can simplify your application’s architecture. This can make your application easier to manage and troubleshoot.

Reduce javascript Code Size

To reduce the amount of JavaScript sent to the browser, you can use the following tools to understand what is included inside each JavaScript bundle:

  • Import Cost — Display the size of the imported package inside VSCode.
  • Package Phobia — Find the cost of adding a new dev dependency to your project.
  • Bundle Phobia — Analyze how much a dependency can increase bundle sizes.
  • Webpack Bundle Analyzer — Visualize the size of webpack output files with an interactive, zoomable treemap.
  • bundlejs — An online tool to quickly bundle & minify your projects, while viewing the compressed gzip/brotli bundle size, all running locally on your browser.

Each file inside your pages/ directory will automatically be code split into its own JavaScript bundle during next build. You can also use Dynamic Imports to lazy-load components and libraries. For example, you might want to defer loading your modal code until a user clicks the open button.

Ensure Sufficient Logging in your Application

Since Next.js runs on both the client and server, there are multiple forms of logging supported:

  • console.log in the browser
  • stdout on the server

If you want a structured logging package, we recommend Pino. If you’re using Vercel, there are pre-built logging integrations compatible with Next.js.

Ensure Proper Error Handling

When an unhandled exception occurs, you can control the experience for your users with the 500 page. We recommend customizing this to your brand instead of the default Next.js theme.

You can also log and track exceptions with a tool like Sentry. This example shows how to catch & report errors on both the client and server-side, using the Sentry SDK for Next.js.

Performance Optimisation

To improve loading performance, you first need to determine what to measure and how to measure it. Core Web Vitals is a good industry standard that is measured using your own web browser. If you are not familiar with the metrics of Core Web Vitals, review this blog post and determine which specific metric/s will be your drivers for loading performance. Ideally, you would want to measure the loading performance in the following environments:

  • In the lab, using your own computer or a simulator.
  • In the field, using real-world data from actual visitors.
  • Local, using a test that runs on your device.
  • Remote, using a test that runs in the cloud.

Once you are able to measure the loading performance, use the following strategies to improve it iteratively so that you apply one strategy, measure the new performance and continue tweaking until you do not see much improvement. Then, you can move on to the next strategy.

  • Use caching regions that are close to the regions where your database or API is deployed.
  • As described in the caching section, use a stale-while-revalidate value that will not overload your backend.
  • Use Incremental Static Regeneration to reduce the number of requests to your backend.
  • Remove unused JavaScript. Review this blog post to understand what Core Web Vitals metrics bundle size affects and what strategies you can use to reduce it, such as:
  • Setting up your Code Editor to view import costs and sizes
  • Finding alternative smaller packages
  • Dynamically loading components and dependencies

In Conclusion, Next Js is the future of web development capabilities and more reason to ensure that more standards are adhered for better performance on all platforms and Devices.

Stackademic 🎓

Thank you for reading until the end. Before you go:

--

--