Server-side rendering with React and Firebase Functions

React SSR with Firebase Cloud Functions

Hello, today I’d like to share how I enabled Server-side rendering with React and Firebase Functions on my own personal website at https://antonybudianto.com.

Update: Content is updated for the latest cra-universal 3.0.0 release, read it here!.

Server-side rendering and Code-splitting with React

So firstly I’ve the following tech stacks:

Actually I’ve been reading article on how to do server-side rendering (SSR) with create-react-app without ejecting it, and it’s great.

After reading the article, I decided to fork the repo to add some customization like Webpack for production build, code-splitting, and separate client and server packages installation, so that it’s portable and reusable for other existing projects.

Well, turned out it worked well. Now you can copy./server into your create-react-app project and it’s done! Now you have a server-side rendering server with Code-splitting support. I also made the cra-universal out of it to ease the copying stuff and build task.

For starter, you can try:

npx create-react-app my-app
cd my-app
npm i -D cra-universal
npx cra-universal start --both

Open http://localhost:3001 in your browser and your create-react-app with SSR is ready!

Firebase functions

Now, we have SSR-ready server, but since Firebase hosting can only host static assets, you can’t really start a node server there.

I looked up on how to do SSR with Firebase, and then I found this cool repo:

https://github.com/firebase/functions-samples/tree/master/isomorphic-react-app

So actually you can host your server with Firebase functions and it’s free! Unless you do outbound networking (Google services are free) within your function.

Firstly you need to change the rewrite on your firebase.json:

Initial:

Change the “destination” key to “function”.

“app” is the export name from ./functions/index.js

Now you need to build the bundle for client and server.

Run the following command:

npx cra-universal build

This command will build both your client and server and produces ./dist folder.

Don’t forget to remove ./build/index.html from client build, because it’ll make server-side rendering not working on root path.

Now create crau-dist folder inside ./functions then copy ./dist contents into ./functions/crau-dist.

Here is my function index.js:

const functions = require('firebase-functions');
const app = require('./crau-dist/server/bundle');
exports.app = functions.https.onRequest(app);

So you need to grab the server bundle and then pass app (which is an express instance) into Firebase function.

Finally, you run firebase deploy to deploy all your Firebase database, hosting, and functions.

To test it, you can use curl https://your-domain.com and check whether your app container (<div id=”root”>) is already populated with pre-rendered markup from server.

For the complete code, you can find it here:

Current challenges:

  • Need to copy the server bundle and client index.html (needed for SSR) into the functions folder manually (for now I used npm script)
  • Firebase function is still in Beta, it might not be stable and the API may change in the future

Feel free to ask or discuss. Thank you for reading!

References: