Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web

How to Deploy Next.js to Firebase

5.00/5 (3 votes)
16 May 2023CPOL10 min read 17.7K   33  
A step-by-step tutorial on how to deploy your Next.js project to Firebase
In this article, we'll cover the entire process of building and deploying a Next.js web app with Firebase Cloud Functions and Firebase Hosting. By following the steps outlined in this post, you should now be able to create and launch your own Next.js app with Firebase.

Introduction

Next.js is a powerful framework for building web applications, built on top of React.js and Node.js. As per Stackoverflow’s 2022 Developer Survey, React.js is the second most loved web framework, right after Node.js. Next.js provides a great set of features for building production-ready applications, such as server-side rendering and static site generation.

Firebase, on the other hand, is an easy-to-use platform by Google that helps you build mobile and web applications.

If you're developing a web application using these two technologies, you're in good company. However, when I was developing Ballistic, I stumbled upon a number of issues when trying to deploy the application. In this article, we'll explore these issues and walk through a step-by-step guide to successfully deploy your Next.js application on GCP.

Next.js: Simplifying SSR and Static Generation

Next.js is a React framework that simplifies the process of building server-side rendered and statically generated web applications. With Next.js, you organize your application into pages and leverage its file-based routing system. When a user requests a page, Next.js pre-renders it on the server, delivering fully rendered HTML content for instant display. Data fetching is made easy with Next.js, supporting server-side data fetching with functions like getServerSideProps and static data fetching with getStaticProps. Next.js also provides client-side rendering capabilities and optimized production builds for improved performance. Its combination of server-side rendering, static generation, and client-side interactivity makes Next.js a powerful framework for modern web development.

Firebase Framework-Aware Hosting for Next.js

Firebase Hosting is a robust and reliable option for hosting websites, particularly when you require rapid and easy deployment. It is a fully-managed hosting service provided by Google, which allows developers to deploy web applications and static content to a global content delivery network (CDN) with a simple command. Firebase Hosting's free tier is particularly appealing for small projects, as it provides a limited amount of storage and data transfer per day at no cost. If your website grows beyond the free tier limits, you can use pay-as-you-go pricing, which allows you to pay only for the resources that you use.

Firebase's framework-aware hosting is a valuable integration that allows for seamless deployment of popular web frameworks such as Angular and Next.js. If you're using Next.js, Firebase will automatically detect your next.config.js file and recognize that you're running a Next.js web application. This results in Firebase translating your Next.js settings into Firebase settings with minimal configuration needed. Additionally, if your app includes dynamic server-side logic, the integration will translate it into Cloud Functions for Firebase. This integration can significantly streamline the deployment process for developers, making it a convenient and efficient option for hosting web applications.

One of the challenges with these "black box integrations" is that when something goes wrong, it can be difficult to troubleshoot. In the case of Next.js and Firebase hosting, an error message such as "Couldn't find a pages directory. Please create one under the project root" can leave developers scratching their heads. When you've verified that the issue isn't caused by your own code, you may be left with no other option than to create an issue on GitHub (if it's an open source project) or contact support and hope for a quick resolution.

That’s why, in this blog post, we’ll explore how to manually translate the Next.js configuration settings to Firebase settings, so we don’t have to rely on the experimental integration of Next.js in Firebase.

Translating Your Next.js App to Firebase

One of the core advantages of Next.js is its support for Server-Side Rendering (SSR), which allows web pages to be pre-rendered on the server, rather than relying on client-side JavaScript to render the page. This translates to a significant improvement in user experience, as the user can view a fully rendered HTML page almost instantly, without waiting for JavaScript or CSS files to load.

By translating your Next.js app to Firebase, you can further enhance its speed and scalability, as Firebase offers a robust and reliable platform for hosting and serving web content. With Firebase Hosting, you can easily deploy and serve your pre-rendered Next.js app to a global audience, providing them with a fast and seamless browsing experience.

To achieve this, we need to divide our Next.js codebase into two parts: the client and the server. The server part will define a Firebase function that handles SSR, while the client part will contain the rest of our Next.js codebase. In the following steps, we'll cover all the details of creating a Next.js project and deploying it to Firebase, from start to finish.

Setting up the Project

Let's begin by setting up our Next.js project by running the following command:

yarn create next-app next-firebase --typescript --eslint --src-dir --no-tailwind 

Accept the default interactive options, and you should see a message saying Success! Created next-firebase. Running this command will create a new Next.js application named "next-firebase" with TypeScript support, ESLint for code linting, a separate source code directory, and without including the Tailwind CSS framework.

This will give you a fully functional Next.js codebase that you can test quickly by starting the development server:

yarn dev

Then, open your browser and navigate to http://localhost:3000.

Creating Server and Client Separation

After successfully setting up your project in the previous step, let's move on to splitting your codebase into a server function responsible for SSR and a client codebase containing all the Next.js code.

Create a client and server folder inside the src directory. Move the original contents of the src directory to the client directory, along with the public folder, next.config.js file, and tsconfig.json file. This will leave the server folder empty for now. We'll populate it with a Firebase function responsible for SSR in the next steps.

The next.config.js file is a configuration file used in Next.js projects. Its purpose is to customize the default behavior and settings of the Next.js framework. By creating and modifying the next.config.js file, you can override the default configuration options provided by Next.js and tailor the project to your specific needs.

The tsconfig.json file is a configuration file used in TypeScript projects. Its purpose is to specify and customize the compiler options for the TypeScript compiler (tsc).

Creating the SSR Server Code

To create the function responsible for server-side rendering, we need to install some dependencies first. Run the following command to install them:

yarn add firebase-admin firebase-functionscd 
yarn add @babel/core @babel/cli @babel/preset-env cross-env rimraf

After installing the dependencies, create an index.js file inside the server folder, as shown below:

JavaScript
import * as admin from 'firebase-admin';
import * as functions from 'firebase-functions';
import next from 'next';

admin.initializeApp();

const dev = process.env.NODE_ENV !== 'production';
const app = next({
    dev,
    // the absolute directory from the package.json file that initialises this module
    // IE: the absolute path from the root of the Cloud Function
    conf: { distDir: 'dist/client' },
});
const handle = app.getRequestHandler();

export const nextjsServer = functions.https.onRequest((request, response) => {
    // log the page.js file or resource being requested
    console.log('File: ' + request.originalUrl);
    return app.prepare().then(() => handle(request, response));
});

The overall purpose of this index.js code is to create a server-side rendering environment using Next.js within Firebase Cloud Functions. When an HTTP request is made to the deployed function, it handles the request using Next.js, allowing server-side rendering of Next.js pages and handling dynamic routing.

Since we'll be transpiling this code with Babel, we need to instruct Babel on how to compile the code. We'll create a .babelrc file inside the server folder for this purpose:

JavaScript
{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": {
          "node": "10.15.3"
        }
      }
    ]
  ]
}

Updating package.json

We need to update the scripts in our package.json to reflect the new folder structure we created:

JavaScript
"scripts": {
    "dev:client": "next src/client",
    "dev:server": "babel src/server --out-dir dist/server --source-maps --watch",
    "dev": "yarn run dev:client & yarn run dev:server",
    "build:client": "next build src/client",
    "build:server": "babel src/server --out-dir dist/server --source-maps",
    "build": "yarn run build:client && yarn run build:server"
}

Let's break these new scripts down one by one:

  1. "dev:client":

    • This script runs the Next.js development server for the client-side code.
    • It starts the development server and specifies the directory where the client-side source code resides (src/client).
  2. "dev:server":

    • This script transpiles the server-side code using Babel and outputs the transpiled code to the dist/server directory.
    • It includes source maps and watches for changes, so it automatically updates the transpiled code.
  3. "dev":

    • This script runs both the client-side and server-side development servers concurrently.
    • It uses the ampersand (&) to run both scripts in parallel.
  4. "build:client":

    • This script builds the Next.js client-side code for production deployment.
    • It triggers the Next.js build process and specifies the directory where the client-side source code resides (src/client).
  5. "build:server":

    • This script transpiles the server-side code using Babel and outputs the transpiled code to the dist/server directory.
    • It does not include source maps.
  6. "build":

    • This script sequentially executes the "build:client" and "build:server" scripts.
    • It builds both the client-side and server-side code for production deployment.

These scripts provide convenience for development and production workflows by automating tasks such as running development servers, transpiling code, and building production-ready code. They enable smoother development and deployment processes for the project.

At this point, you can spin up a local development server that hosts your client side code and serves your SSR server. You can also build a production-ready version of your Next.js app.

Also add the following lines to your package.json, in order to override the main entrypoint to your server:

JavaScript
"main": "dist/server/index.js",
"engines": {
  "node": "16"
},

These lines ensure that when you deploy your app to Firebase, it uses the compiled version of your server code instead of the original source code. They also specify that the app requires Node.js version 16 to run.

Setting up Firebase

To integrate Firebase with our Next.js app, we'll need to create a new Firebase project in the Firebase console. If you already have a project set up, you can skip this step. Once you've created your project, you can add Firebase to your web app by navigating to the Project Settings page, clicking on the gear icon next to Project Overview, and selecting Add to Web App on the General tab. Here, you can register your new web application.

Next, in the root directory of your project, run the following command to initialize Firebase:

firebase init

In the root directory of your project, during Project Setup, select the following options:

  • Firebase Hosting and Firebase Functions
  • Use an existing project, and select the firebase project you just created or that you wish to use
  • What language would you like to use to write Cloud Functions? → JavaScript
  • Do you want to install dependencies with npm now? → No
  • Detected an existing Next.js codebase in the current directory, should we use this? → No
  • Do you want to use a web framework? (experimental) → No
  • What do you want to use as your public directory? → Public
  • Configure as a single-page app (rewrite all urls to /index.html)? → No
  • Set up automatic builds and deploys with GitHub? → No

After running this command, a .firebaserc file and a firebase.json file will be created in your root folder. You'll need to modify the firebase.json file to include the following code:

JavaScript
{
  "hosting": {
    "public": "public",
    "rewrites": [
      {
        "source": "**/**",
        "function": "nextjsServer"
      }
    ]
  },
  "functions": {
		"runtime": "nodejs16"
    "source": ".",
    "ignore": [
      ".firebase/**",
      ".firebaserc",
      "firebase.json",
      "**/node_modules/**",
      "**/public/**",
      "**/cache/**"
    ]
  }
}

This configuration sets up hosting for the static files in the "public" directory and defines a rewrite rule to direct all incoming requests to the "nextjsServer" Cloud Function. It also configures the Cloud Functions runtime, specifies the source code directory, and defines the files and directories to be ignored during deployment.

The previous process also generates a public folder with a 404.html file and an index.html file. Make sure to delete the index.html file before continuing.

Test Your Website and Deploy

At this point, you can test your web app locally, share your changes with others, and deploy the app to the public. To test your app locally, you can use the serve script from package.json:

yarn serve

After verifying that your web app works fine locally by running the serve script from package.json, you're ready to deploy it to the public. To do this, you need to add the necessary deployment scripts to your package.json file:

JavaScript
"scripts": {
    "predeploy": "rimraf dist/ && yarn run build",
    "deploy": "cross-env NODE_ENV=production firebase deploy --only functions,hosting",
 }

Now, when you're ready to deploy your app, you can simply run yarn deploy to deploy your app to Firebase hosting.

Conclusion

In conclusion, we have covered the entire process of building and deploying a Next.js web app with Firebase Cloud Functions and Firebase Hosting. By following the steps outlined in this post, you should now be able to create and launch your own Next.js app with Firebase.

The full source code for this project is available on GitHub at https://github.com/GlennViroux/next-firebase-blog.

Thank you for reading, and I hope this article has been informative and helpful for you.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)