How to deploy Next.js to Firebase
Tags: nextjs, react, tutorial, firebase
Published: 2023-05-05 10:50:48

**[Next.js](https://nextjs.org/)** is a powerful framework for building web applications, built on top of React.js and Node.js. As per **[Stackoverflow’s 2022 Developer Survey](https://survey.stackoverflow.co/2022/)**, 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](https://ballisticapp.io/)**, 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.

## 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:

```bash
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`**.

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

```bash
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.

## 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:

```bash
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:

```jsx
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));
});
```

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:

```json
{
  "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:

```json
"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"
}
```

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:

With these changes, 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.

Additionally, add the following lines to your **`package.json`** to override the main entry point to your server:

```json
"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:

```bash
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:

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

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`**:

```bash
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:

```json
"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](https://github.com/GlennViroux/next-firebase-blog)**.

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