Andrew Vo-Nguyen
June 9, 2021
5 min read
Ever since I learnt how to create websites using React, my go to boilerplate starter code was to use Create React App. It is the easiest way to learn React and the fastest way to get a website up and running. After creating my own website using Create React App, I quickly found the pitfalls of having a client rendered SPA (Single Page Application).
The first issue I had was that the whole application needed to be downloaded before being rendered. For the user, this means a white screen until the JavaScript code was downloaded. The second issue I found was that there was no way to do SEO (Search Engine Optimisation) or to share single pages to sites such as Facebook or LinkedIn. Because Create React App is rendered completely inside a single root div
. This means that the HTML page remains unchanged (including everything inside the <head>
tags). As Create React App traditionally uses React Router to direct its users to various "pages", these "pages" are unable to be seen by search engine crawlers. The last issue I experienced was that the data on my website rarely changes (such as my Projects page and Blog page), however every time someone visits the page, it required a new API call for the same data over and over again.
With these 3 issues came the adoption of Next.js. I spent about 3 weeks learning Next.js on Udemy, course link:
The course taught me everything I needed to know about Next.js including file based routing, pre-rendering, data fetching, static site generation, API routes, deployment and much more. I found learning Next.js (coming from Create React App) slightly uncomfortable, from re-learning routing, to programming the server side code (in Node.js all in the same codebase), to deployment. Next.js is a completely different ball game and brings an array of new features to your traditional React App.
Faster First Paint Load Times
As my pages are now pre-rendered from the server as static pages, when a user visits my website, the entire React App is not loaded. Only the requested page is loaded with the necessary HTML and JavaScript. Next.js provides great optimisation features out the box such as lazy loading, code splitting and automatic image optimisation.
Dynamically Change HTML Meta Tags
Once again pre-rendering allowed me to dictate what meta content to display in my HTML <head>
tags. This is great for Search Engine Optimisation as it allows each individual blog page to have its own tags.
Save On Database Reads
As mentioned above, my website rarely changes. It only changes when I create a new blog post or update any personal information on the "About" page. Because I am using a database as a service, it is ideal to keep the amount of reads to a minimum to stay in the free tier. For example, using the old client side fetching method means that if a million people were to visit my blog, that would require a million database reads as it would need to fetch the same data from the database each time. Using static site generation, the database read occurs once at build time and then the page is statically served. The great thing about Next.js is that it supports multiple methods of data fetching, whether it's at build time, "just in time" on request or the traditional client side data fetching pattern (using useEffect
or in componentDidMount
). They also provide an awesome client side fetching hook called useSWR
which handles errors and caching for you.
Running A Full Stack App In The Same Codebase
This may seem counter intuitive to separating responsibilities of the backend and the frontend, and I'm not 100% sure how I feel about this architecture. All I know is, for my small website, it was super convenient. Next.js supports API routes. This means you literally create a REST API inside your React codebase. Previously, I had a separate code base to run Firebase Functions to handle actions such as new Blog Post comments and the sending of emails when somebody fills out the Contact Me form. I now replaced these two actions with API Routes. When Next.js builds, it detects your backend code and frontend code and separates the two. Don't worry, no backend code is exposed to the client side. The backend runs Node.js and give you access the filesystem, elevated rights to the database etc.
Easy Deployment To Vercel
To stay in the Firebase ecosystem, I initially tried to deploy the Next.js app using Firebase Hosting accompanied by Firebase Functions for server side rendering. Following a few online guides on how to do this, I failed miserably. Because of the nature of cloud functions going to sleep and cold starting, it was not ideal to serve pages on the fly. This affected the user experience. I decided to try Vercel. Vercel is a hosting service, I believe made from the same mob that make Next.js. It is optimised and designed to run Next.js apps. They provide frontend static hosting as well as a backend Node.js server for all your server side actions. Installing the Vercel CLI was a piece of cake and deployment was even easier (They call their deployment "zero configuration", and I couldn't agree more). Best of all, it is free with no credit card required.
I am glad I converted my website from Create React App to Next.js and as far as I can tell Next.js does live up to the claim that it is a production ready framework. It can do everything that a regular Create React App can do plus more. Moving forward for new projects, I will most likely choose Next.js over CRA, even if I don't require server side rendering. The file based routing seems more natural and intuitive than using React Router, automatic code splitting and lazy loading is a big plus and the great TypeScript support makes it a no brainer.