Limitations of create-react-app
Once you start learning web development, sooner or later you'll learn React and use create-react-app
to kickstart the building of your first React app. Or at least that's what CodeCademy taught me to do (in 2019). And I built my first React app, Line-height Picker, out of create-react-app
.
However, I've noticed web developers often point out two limitations of create-react-app
: (1) it takes time for the landing page to be rendered; and (2) search engine crawlers may fail to index the app.
In addition, what keeps bugging me while I'm building an app from create-react-app
is this: (3) it doesn't show anything meaningful to the user who surfs the web with JavaScript disabled.
All these limitations stem from the reliance of create-react-app
on client-side rendering (often abbreviated as CSR), where the app-hosting server sends the JavaScript code to the user's browser, which then runs the received JavaScript code to generate HTML documents.
Here's how CSR affects each of the three above-mentioned limitations of create-react-app
.
1. The landing page appears slowly
With an app built with create-react-app
, it takes time for the landing page to appear on the user's browser.
Figure 1: The Lighthouse audit result of Line-height Picker, a React app that I made out of create-react-app
. It takes 3.2 seconds for the landing page to appear.
It is because, once the user's browser receives the data from the server, it needs to run the JavaScript code to generate the HTML document that the user can see. While the browser is busy working on rendering the page content, the user keeps staring at a blank page. It clearly doesn't contribute to a good user experience.
2. Search engines fail to index your web app
In addition, the search engine optimisation (SEO) can severely be compromised with CSR, as explained by Miller and Osmani (2019). Search engine crawlers may fail to run JavaScript to render your React app. So your React app is indexed as a blank page, which clearly doesn't help the user to find your React app by search.
3. JavaScript-disabled users will see nothing
The third limitation with create-react-app
is that none of the "meat" of your React app will be shown if the user disables JavaScript on their browser.
Installing create-react-app
will create the file called /public/index.html
which contains the following code inside the body element:
<noscript>
You need to enable JavaScript to run this app.
</noscript>
The message enclosed in the noscript
tags will be shown when the browser disables JavaScript (MDN Contributors 2020).
Ideally, however, the non-interactive components of your React app should be shown to the JavaScript-disabled users. This way, they will be able to see what the app is like, before deciding whether to enable JavaScript or not.
A workaround is to insert an HTML version of your React app into the noscript
tags. But this approach is super tedious: whenever you revise the React code, you have to manually change the HTML code as well.
Most web developers appear to dismiss this concern as irrelevant today; even people in less developed countries use a JavaScript-enabled device to browse the web (see Lawson 2016).
However, when I read various comments to a Smashing Magazine article about surfing the web without JavaScript (Ashton 2018), I've learned that a non-negligible number of people actually disable JavaScript when they surf the web.
Let me cite a few of these comments. Matt wrote as recently as on 18 April, 2020:
"I'm … browsing the Web with [JavaScript] turned off. Things run faster. It's also good for bypassing pay walls on news articles and I don't get so many popups anymore"
Phillip Parr also wrote on 9 March, 2019:
"I regularly browse the web with JS disabled. Too many sites are using JS now 'just because', and overloading my connection / CPU time with frivolous multi-megabyte framework downloads. It's VERY easy to build a fast, efficient, valid, accessible site with no JS at all, and I absolutely implore everyone to do so. Quite a few sites are completely broken with JS disabled, despite being perceptually static when enabled."
As a percentage, these JavaScript-disabled users are a tiny minority. Deliberate Digital (2016) reports that 0.2% of the worldwide page views had JavaScript disabled during October to December 2016. That's a similar percentage to the global page views with Internet Explore 9 in August 2020 (Can I Use 2020).
But reading their own voices—rather than just the cold number of "0.2%"—makes me feel that we should not ignore them. The web content is for everybody. Access to information doesn't require personal connections with knowledgeable people. That's the beauty of the web.
Now that we understand the limitations of create-react-app - or client-side rendering (CSR) in general - the question is: how can we do better to build a React app?
Solution: Pre-rendering
The answer is pre-rendering, which may involve static generation, server-side rendering (often abbreviated as SSR), or both.
These jargons are often used without clear explanation in web dev articles. I myself was confused a lot, until I read a crystal-clear description by Vercel (2020), the official tutorial of Next.js (more on Next.js below).
Here is my own understanding of what pre-rendering is and how it solves the limitations of client-side rendering (CSR) while preserving the merits of React.
Merits of React
The whole problem stems from the fact that the server sends JavaScript code, not HTML documents, to the user's browser. React takes this approach for two reasons.
First, it ensures fast user interactions with the app. The browser doesn't need to contact with the server whenever the user clicks, taps, or drags the app's interface: all the JavaScript code for user interactions has already been downloaded and can be run on the browser.
Second, the use of JavaScript to render HTML documents makes it easier to maintain web pages thanks to the modular design of React Components.
These two merits come at a cost of the three limitations of create-react-app described above.
How pre-rendering works
Pre-rendering is a solution to overcome the limitations while keeping the two merits of React. It runs JavaScript code to generate HTML documents before sending any data to the user's browser. That's what "pre-rendering" means.
The pre-rendered HTML documents will be sent along with the JavaScript code for interactivity so that the first merit of React is preserved.
Web developers can still use JavaScript to compose HTML documents, the second merit of React.
Pre-rendering method #1: Static generation
One form of pre-rendering is static generation, the most popular tool for which has been Gatsby, a static generation framework for React-based web development. I kept hearing its name for powering "blazing fast" websites, without knowing what Gatsby was special about. Now I know why.
Static generation converts your React JavaScript code into HTML documents before you deploy your React app. This has two consequences.
First, when the user visits your web app, the server sends the pre-rendered HTML document to the user's browser. Consequently, the user will save time for rendering HTML documents by running JavaScript on their browser.
Second, even when the user disables JavaScript, he or she still sees the landing page instead of a cold message "Please enable JavaScript". So does the search engine crawler.
This is a great solution for websites such as blogs which do not involve interactive features other than hypertext links. You can use React to compose HTML documents without sacrificing the speed of rendering the landing page.
Aside from Gatsby, static generation can be implemented with Next.js since its version 9.3, released on March 10, 2020 (Neutkens et al. 2020a). Below we compare these two options for static generation in the final section of this article.
If you have already created a React app with create-react-app
, refactoring the code for Gatsby or Next.js is a big headache. In this case, consider Navi, which allows you to convert the code based on create-react-app
into a statically generated one.
Pre-rendering method #2: Server-side rendering (SSR)
Another form of pre-rendering is sever-side rendering (SSR), which deals with a drawback of static generation at the cost of a slower rendering speed. Di Mattia (2021) concisely explains how server-side rendering works, with nice illustrations.
Static generation cannot work with live data such as social media feed, because HTML documents were already created before deployment.
SSR can handle live data because it converts your React JavaScript code into HTML documents when the user accesses the website.
The cost of doing so, of course, is that the user needs to wait while the server runs the JavaScript code to generate HTML documents. However, unlike static generation, the latest pieces of data (e.g. social media posts) can be incorporated into the HTML documents delivered to the user.
As far as I can tell, Next.js has long been the React framework for SSR, and it still is.
Gatsby vs Next.js
For static generation, you need to decide which framework to go with, Gatsby or Next.js. Here are some pieces of information to help you make a choice.
There are a countless number of articles that compare these two React frameworks. But I advise you to ignore all of those written before March 10, 2020, because Next.js was incapable of static generation until then (Neutkens et al. 2020a).
Cases for Gatsby
In an article written one month after the release of Next.js 9.3, sidney (2020) claims "Gatsby Won Against Next.js" after he himself built the same website with both frameworks. LightHouse performance scores are slightly higher for Gatsby (78 vs 74). He also mentions that documentation is better with Gatsby.
But this is the only article that I've found is in favor of Gatsby.
Gatsby's own website provides the comparison chart between the two (Gatsby 2020). Unsurprisingly, it claims that Gatsby provides more features than Next.js, although it is unclear which version of Next.js they refer to. As Next.js keeps updating itself, most recently on Oct. 27th, 2020 (Neutkens et al. 2020b), this comparison chart may be outdated by now.
Cases for Next.js
Laing (2020), written one month later after Next.js becomes a static generation tool, argues that Next.js is a better option because of its SSR capability. Maybe you start out building a static website. But then when you realize you need SSR, Next.js just allows you to implement it while Gatsby does not. For each feature that he mentions Gatsby is better at, there is a comment to this article saying Next.js, too, has that feature.
In the Twitter sphere, Next.js appears to get more popular.
A Twitter poll by Buaiscia (2020) on July 6, 2020, shows that 7 out of 13 voted for Next.js as a blogging platform while 5 voted for Gatsby.
McDaniel (2020), tweeting on August 4, 2020, is in favour of Next.js:
"Switching this new site to #nextjs from #Gatsby. The more I started getting everything together the more I realized next js was going to be a better choice. Love me some gatsby though."
The NPM weekly download data backs up this trend:
A screenshot of NPM trends on November, 30, 2020
The popularity of Next.js is on the rise from around 400,000 to 1,000,000 downloads per week while Gatsby's is stagnated around 400,000 per week.
Of course, the number of package downloads doesn't mean the number of people who actually keep using it. But it is an indication of reputation. People won't download it unless they hear something good about the package.
Verdict
As of November 2020, Next.js appears to be more suitable for a static generation tool.
If you want to decide which to use on your own judgement, instead of relying on what people say, Smashing Magazine recently interviewed the person behind each React framework, for the audience who doesn't even know what static generation is. Listen to each's sales pitch, and decide which one you will go with.
- Smashing Podcast Episode 20 With Marcy Sutton: What Is Gatsby? - Smashing Magazine
- Smashing Podcast Episode 23 With Guillermo Rauch: What Is Next.js? - Smashing Magazine
Update on 2 August 2021: Gatsby now supports SSR with the introduction of Gatsby Functions, released in June 2021 (Gatsby 2021). See Scanlon (2021) for detail and how to use it.
This article is part of Web Dev Survey from Kyoto, a series of my blog posts on web development. It intends to simulate that the reader is invited to Kyoto, Japan, to attend a web dev conference. So the article ends with a photo of Kyoto in the current season, as if you were sightseeing after the conference was over.
So let me take you to the world's famous zen garden at Ryoan-ji Temple:
Ryoan-ji Temple Rock Garden at 8:26 am on 29 November, 2019. Photographed by Masa Kudamatsu (the author of this article)
Hope you have learned something today! Happy coding!
Footnote
I use the Author-Date referencing system in this article, to refer to various articles on web development.
References
Ashton, Chris (2018) "I Used The Web For A Day With JavaScript Turned Off", Smashing Magazine, May 8, 2018.
Buaiscia, Alex (2020) "A Tweet on July 6, 2020", Twitter.
Can I Use (2020) "Browser Usage Table" caniuse.com, 8 September, 2020.
Deliberate Digital (2016) "What percentage of browsers with javascript disabled?", deliberatedigital.com.
Di Mattia, Sandrino (2021) "The Ultimate Guide to Next.js Authentication with Auth0", Auth0 Blog, Mar 3, 2021.
Gatsby (2020) "Comparison of Gatsby vs Next.js", gatsbyjs.com.
Gatsby (2021) "v3.7 Release Notes", gatsbyjs.com, June 2021.
Laing, Malcom (2020) "Which To Choose in 2020: NextJS or Gatsby?", Frontend Digest, Apr. 18, 2020.
Lawson, Nolan (2016) "Progressive enhancement isn't dead, but it smells funny", Read the Tea Leaves, Oct. 13, 2016.
McDaniel, Josh (2020) "A Tweet on August 4, 2020", Twitter.
MDN Contributors (2020) "<noscript>", MDN web docs, Apr. 12, 2020.
Miller, Jason, and Addy Osmani (2019) "Rendering on the Web", Web Fundamentals, Nov. 26, 2019.
Neutkens, Tim, Joe Haddad, JJ Kasper, Luis Alvarez, and Shu Uesugi (2020a) "Next.js 9.3", Next.js Blog, Mar. 10, 2020.
Neutkens, Tim, Joe Haddad, JJ Kasper, Connor Davis, Luis Alvarez, Shu Uesugi, Belén Curcio, and Steven (2020b) "Next.js 10", Next.js Blog, Oct. 27, 2020.
Scanlon, Paul (2021) "Gatsby Serverless Functions And The International Space Station", Smashing Magazine, Jul 26, 2021.
sidney (2020) "Gatsby Won Against Next.js in this Heads Up Competition", Hacker Noon, Apr. 27, 2020.
Vercel (2020) "Two Forms of Pre-rendering", Next.js Docs.
Top comments (4)
Thanks for sharing! This is random, but I knew that garden just from the title picture before I scrolled down. Getting to visit it had a big impact on me. Thanks for bringing me back there 🙂🍁
I can also tell the amount of effort that went into this post, and all the work you did to respect others efforts, wonderful!
BTW, you can tag code blocks with a language to get syntax highlighting by adding the language extension (e.g.
js
,c
,cpp
,java
) after the the first three backticks.Thank you for posting a comment, Devin!
I wasn't sure if it would be a great idea to add a picture completely irrelevant 😅 to web development, but I'm glad it actually gave you a positive impact. 😊 I've been to this garden whenever I need an escape from reality. It helps me recover tranquility in my mind.
Thank you for noticing my effort to "respect others efforts". Coming from academia, I believe that clarifying how knowledge is accumulated on top of each other's writings will help the reader understand the content a lot better than otherwise.
I've just revised the code block in this article by following your advice. Thanks a lot!
Two questions:
Next also supports automatic static optimization with SSR, where it prerenders the page at build if it doesn't explicitly use SSR features such as on the fly data fetches, and caches it on runtime.