Next.js alternative (SSR)? Try NestJS + Nunjucks + Tailwind CSS

Next.js alternative

Next.js is a popular framework based on React, which allows building applications with server-side rendering (SSR) and static site generation (SSG). The application code is written in TypeScript. Next.js has gained popularity due to its ease of creating applications that use server-side rendering, as well as support for Static Site Generation (SSG), which allows for building static versions of websites that can be quickly served to users. However, for some projects, exploring a Next.js alternative might be worthwhile.

Recently, I created a list of developer blogs using it, and I have to admit, it’s a great solution, especially when it comes to deploying the application on the vercel.com platform. The deployment process was quick and intuitive, which makes Next.js very attractive for such projects. Additionally, creating this website was a test of whether Next.js is the optimal solution for my next project, which will be available in most of the world’s languages.

However, although Next.js is a powerful tool, it is not always the optimal solution for all projects. That’s why I started considering what Next.js alternative might be the best fit for my projects. For example, the framework automatically adds additional data, such as JSON files, to the HTML code, which contains the information necessary for recreating the application state in the browser. This process, while useful in some cases, can lead to sending more information than necessary.

For instance, when creating a multilingual website, Next.js often sends all translations (for multiple languages) to the browser, even if the user is viewing only one language version of the page. Next.js places this data in the page source as JSON files, aiming to enable instant language switching or client-side state reconstruction. The issue arises when a website supports multiple languages — all translations might be sent to the browser, even if the user is only viewing one language version, e.g., English.

For example, let’s assume your website supports 10 languages. In that case, Next.js might generate JSON files that contain all the translation versions, even those not currently displayed. This leads to redundant data being transferred, which increases the page size and thus the loading time. A user visiting the page in English does not need translation data for French, German, or Japanese, yet these are still sent. This is where considering a Next.js alternative can help avoid such inefficiencies by ensuring only the necessary data is delivered.

This can significantly impact the performance of the site, especially for users with slower internet connections or using mobile devices. A larger amount of data to download means longer loading times, negatively affecting user experience (UX) and the site’s ranking in search engine results (SEO).

Therefore, if you want full control over what data is sent and how the page is generated, it’s worth considering alternative solutions to Next.js, such as NestJS combined with Nunjucks or another template engine. In this approach, rendering takes place entirely on the server-side, and only the necessary data for the currently displayed version of the page is sent to the browser. This way, you can avoid the issue of redundant data, and each language version of the site will be optimally loaded and fast.

One such approach is the combination of NestJS with Nunjucks and Tailwind CSS, which offer greater flexibility and control over the page generation process. In this article, we will discuss why it’s worth considering these technologies when creating global, multilingual projects that can potentially generate high traffic. With the combination of Nunjucks and Tailwind CSS, it is possible to build fast, flexible, and scalable applications that resemble classic server-side solutions, like those known from PHP, but with modern tools and greater control over performance.

Why does NestJS + Nunjucks resemble PHP?

If you have ever used PHP to build websites, you will likely remember how the page rendering process works. PHP scripts dynamically generate HTML on the server and send it to the user’s browser. Similarly, NestJS with Nunjucks allows HTML pages to be rendered on the server based on data, giving you greater flexibility and control over what and when gets generated.

NestJS is a modern Node.js framework designed for scalable and efficient server-side applications. It is based on a modular architecture, which simplifies managing complex projects, similar to backend frameworks like Spring in Java or ASP.NET in the Microsoft ecosystem. NestJS uses TypeScript and offers built-in support for server-side rendering, making it an excellent choice for building API-based applications, web applications, or backend systems.

In terms of HTML page rendering, NestJS works with various template engines, one of the most powerful and flexible being Nunjucks. Nunjucks is a versatile template engine inspired by Jinja2 from Python, offering advanced features such as template inheritance, macros, loops, conditions, and dynamic data insertion. With Nunjucks, you can easily manage complex HTML structures, render dynamic content, and control the logic for displaying elements on the page. It is an ideal solution for building global applications that need to handle many components and dynamic content in different languages.

If NestJS with Nunjucks seems like the right solution and a better alternative to Next.js for you, it’s also worth knowing that there are many other template engines available. Engines like Pug, Handlebars, Mustache, or EJS offer different features and approaches to server-side rendering. The choice of the right engine depends on the complexity of the project, the structure of templates, and syntax preferences.

Main Benefits of NestJS + Nunjucks + Tailwind CSS over Next.js (SSR)

1. Greater control over the rendering process

Next.js automates many aspects of server-side rendering, which for some projects can be limiting. With NestJS and Nunjucks, you have full control over every step of the rendering process — from database access to generating the final HTML. This way, you can optimize pages exactly according to your needs.

2. Fewer redundant data

In Next.js (SSR and SSG), during page generation, besides static HTML code, data in the form of JSON files is also added to the page source. These are needed to recreate the state of the application on the client-side, which is typical for React applications. However, this approach can lead to sending more data than necessary, slowing down the page. More data being sent not only means longer loading times but also higher costs if you use cloud services like Azure, AWS, or Google Cloud. Depending on the solutions chosen, data transfer often incurs additional charges, especially with a large number of users and high traffic on the site.

With Nunjucks in NestJS, you only generate what is actually needed at the moment, which minimizes the amount of data being transferred. This way, not only do you improve page performance, but you also reduce data transfer costs, which in the long run can bring significant savings, especially in large applications or high-traffic websites.

After presenting the benefits of combining NestJS with Nunjucks and Tailwind CSS, let’s now move on to the practical part. Below you will find a complete step-by-step guide that will walk you through setting up the environment, installing the necessary packages, and preparing the project structure.

Step 1: Install Nest.js

Install the NestJS CLI globally if you haven’t already:

npm i -g @nestjs/cli

Create a new NestJS project:

nest new name-of-your-project

Step 2: Configure Nunjucks

Install the Nunjucks template engine:

npm install nunjucks

Configure Nunjucks as the view engine in the main.ts file:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { NestExpressApplication } from '@nestjs/platform-express';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);

  // Set the views directory and Nunjucks engine
  nunjucks.configure('views', {
    autoescape: true,
    express: app,
    watch: true,
  });
  app.setViewEngine('njk');
  // Serving static files from the 'public' directory
  app.useStaticAssets(join(__dirname, '..', 'public'));
  await app.listen(3000);
}
bootstrap();

Step 3: Configure Tailwind CSS

Zainstaluj Tailwind CSS oraz wymagane zależności:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init

Configure the tailwind.config.js file:

module.exports = {
  content: [
    './views/**/*.njk',
    './public/**/*.js',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
};

Create a Tailwind CSS file in the src/css/ folder, e.g., src/css/styles.css, and add the Tailwind directives:

@tailwind base;
@tailwind components;
@tailwind utilities;

Configure PostCSS by creating the postcss.config.js file:

module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

Create the views folder. In the Nunjucks files, add a link to the generated CSS file (e.g., in views/index.njk):

<link href="/styles.css" rel="stylesheet">

Process Tailwind to generate the final CSS file:

npx tailwindcss -o public/styles.css

Step 4: Create a controller to render the homepage

In the controller file (e.g., app.controller.ts), create a method that will return the Nunjucks view. Here’s an example controller code:

import { Controller, Get, Render } from '@nestjs/common';

@Controller()
export class AppController {
  @Get()
  @Render('index') // Refers to the 'index.njk' file in the 'views' folder
  getHome() {
    return { title: 'Strona główna' }; // You can pass data to the Nunjucks view
  }
}

In the index.njk file, you can add simple HTML code to display the homepage:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <link href="/css/styles.css" rel="stylesheet">
  <title>{{ title }}</title>
</head>
<body>
  <h1>Hello!</h1>
</body>
</html>

Run the application and go to the homepage (http://localhost:3000/). You should see the page with the content of the index.njk file.

To summarize:

  • Nunjucks views are placed in the views folder.
  • The homepage is the index.njk file, and the controller is responsible for rendering this page.

Alternative to Next.js: Summary

If you want, you can expand this template with additional sections and components, and fully utilize Tailwind CSS for styling. I believe that the combination of NestJS, Nunjucks, and Tailwind CSS is an excellent alternative to Next.js, especially for projects that require full control over the rendering process and minimizing transferred data.

In the next post (or by updating this one), I will show how to expand this application by adding integration with MongoDB. This will allow us to manage data in a database, and the application will become fully dynamic and ready to handle larger projects. I will also show how easy it is to integrate MongoDB with NestJS using the @nestjs/mongoose module, and how to create CRUD (Create, Read, Update, Delete) operations for more advanced applications.

Follow the upcoming posts to learn more!