Disclaimer: Please note that this article is from our old blog site. This article was originally published on November 10th, 2020 and the original author of this article is Josh McFarlin.
In 2020, there are a lot of frameworks and tools for creating websites. If you wanted to create a new website using React, the most common starter is Create React App.
While I do believe Create React App is a great way to start coding in a short amount of time, in my opinion, it’s time to move on.
Why?
Create React App has a lot of things going for it. You can easily make a new project using the command:
npx create-react-app my-app
and be ready to code in less than a minute. But there is much more to choosing a framework than ease and speed of setup.
One of the most important things I have learned as an Engineering Manager at Bits of Good is the importance of architecting projects correctly from the start. While it’s tempting to jump directly into development to get work done in the shortest amount of time possible, it’s not worth the risk of running into roadblocks mid-project due to making the wrong choices during planning.
When evaluating libraries to use in projects, I often consider the speed, reliability, and features offered. In this case, speed not only refers to the performance of the library but also the speed of development. While a library might be quick to implement, in many cases it does not have long-term reliability or the features needed for all functionality. While it’s always possible to add more libraries to fill in the missing gaps of those already implemented, a much wiser choice is choosing libraries that offer the desired features and reliability from the start.
While Create React App is a solid starter for React apps, it has considerable issues when it comes to size, performance, and user experience. While developers might overlook these issues as nonimportant at the beginning of their project, they can turn into a huge headache as the project matures. Thankfully, there are a growing number of React starters seeking to solve these issues and allow projects to easily scale.
Introducing Next.js
If you have been working in the React ecosystem for a while, you have likely already heard of Next.js. Its numerous advantages over other starters are no secret, and Next.js boasts its usage by Hulu, Netflix, GitHub, Twitch, and more!
Next.js gives you the best developer experience with all the features you need for production: hybrid static & server rendering, TypeScript support, smart bundling, route pre-fetching, and more. No config needed.
And its true, Next.js has a setup process just as easy as Create React App:
npx create-next-app my-app
While the command is just as simple as Create React App’s, the improvements and new features offered by Next.js bring the former’s lead into question.
While I can talk about Next.js forever, I will limit this article to the major differences from Create React App:
Rendering
The content of websites is represented by the Document Object Model (DOM), which consists of the content on the page and the interface needed to interact with it, such as getElementsByClassName
in JavaScript.
React’s rendering works by using a virtual DOM, which is a lightweight visual representation of the full DOM. Because the VDOM is only a visual representation and lacks the large API of the full DOM, changes in the page are calculated much faster and are then compared with the full DOM to make the minimum number of changes required.
Create React App leverages this VDOM using a process called Client-Side Rendering (CSR), which generates an empty HTML file including a large JavaScript file that needs to be loaded before the entire website can be shown. This means everything that needs to be shown on the page is calculated when the browser accesses the website, before the user goes to the website it is essentially empty.
There are several issues with this approach:
The JavaScript generated by CRA is massive. When you first access the website, the large JavaScript file has to be downloaded, which can result in a noticeable delay and annoying flash as page content is generated.
Devices need JavaScript to visit the website. While the number of devices without JavaScript is very low, any client visiting the app without JavaScript is only shown the text
You need to enable JavaScript to run this app
.Poor search engine ranking. Create React App is a Single-Page Application (SPA), meaning the entire website is actually one page. When you visit a path in the app, such as
example.com/hello
, you are actually being redirected to the indexexample.com
, and then React gets the pathhello
from the URL and determines the content that needs to be loaded.Not all search engines run JavaScript while indexing the page. Thankfully, Google and other major search engines now support JavaScript, but the pages are indexed much slower and there is always a chance that smaller search engines might not run JavaScript.
Thankfully, there are alternatives to Client-Side Rendering called Server-Side Rendering (SSR) and Static-Site Generation (SSG). These processes generate a basic HTML file from the React code that contains a pre-generated version of what the page will look like after it has been rendered. The JavaScript file containing the React code is then separated from the HTML and is only loaded after the page itself has been loaded, a process called hydration.
This means the user is served a viewable version of the website extremely quickly, and the slower interactive version is loaded in the background without them noticing. The difference between SSR and SSG is that SSG generates static HTML files that are stored and served to every user. In contrast, SSR generates the HTML for each user on the server and serves it to the user directly, meaning pages can be customized to each user.
As pages have already been created before being visited by a user, they load much faster. One benchmark shows that while a CSR website (Create React App) loads in 6.5 seconds, the SSR version created with Next.js loads in only 0.8 seconds!
So what does this have to do with Create React App? Unfortunately, Create React App is a single-page app (SPA) meaning it is CSR. While there are actually ways to get around this limitation, its performance still suffers. Furthermore, purpose-made solutions for SSR and SSG like Next.js offer an incredible number of new and exciting features.
Data Fetching
Another huge advantage of SSR and SSG is the ability to fetch data before a page is loaded.
To use data from an API within a CSR app, the following implementation would likely be used:
const Page = () => {
const [data, setData] = React.useState(null); React.useEffect(() => {
fetch("apiUrl")
.then((res) => res.json())
.then((json) => setData(json));
}, []); return (
<div>
<h1>Hello World</h1>
<p>{data}</p>
</div>
);
};
The issue with this is that the page will be rendered only displaying the text “Hello World”, make the API call, and then rerender once the response data has been set. This means the <p>
tag will flash when the data renders, causing a distracting experience for users.
Thankfully, Next.js has the options getStaticProps
and getServerSideProps
, allowing data to be fetched before the page has even been rendered and then provided to the component as props. This means the data is already there when the page loads, presenting a far superior experience because there are no loaders or annoying flashes while waiting for data to load.
const Page = ({ data }) => {
return (
<div>
<h1>Hello World</h1>
<p>{data}</p>
</div>
);
};export async function getStaticProps() {
const res = await fetch("apiUrl");
const data = await res.json();
// Pass data to the page via props
return {
props: {
data,
},
};
}
To demonstrate these rendering methods, I created an example application. View the CodeSandbox below or the production version hosted on Vercel.
Notice the improved experience of SSR/SSG over CSR. While the CSR page has a delay and visible flash of data, the SSR and SSG pages offer immediate rendering with the data already loaded. This presents a much better experience for users as they don’t need to wait for the data to load before interacting with the full page. In this example, only a single source of data is being used within pages, but the results are even better in cases using multiple data sets.
Server-Side Rendering vs Static-Site Generation
SSR and SSG both offer a far superior experience to the user over CSR, but it's good to know when to use each. The difference between SSR and SSG is that SSR pages are rendered on the server for each unique request. In contrast, SSG pages are rendered one time when the website is built, and the same page and data are served to all requests.
If each request requires its own unique data (such as returning the profile data for a currently logged in user), use server-side rendering. A common approach for this is storing session cookies, which can be checked in API requests andgetServerSideProps
to return data unique to the current user.
In contrast, if a page uses the same data for each request (such as a list of public event dates), use static-site generation. This is incredibly useful when data is expensive or uses lots of resources to retrieve, as it only needs to be done once.
Another advantage of SSG using Next.js is the ability to update SSG pages on a schedule in a process called incremental static regeneration.
export async function getStaticProps() {
const res = await fetch('https://.../posts')
const posts = await res.json()
return {
props: {
posts,
},
// Next.js will attempt to re-generate the page:
// - When a request comes in
// - At most once every minute
revalidate: 60, // In seconds
}
}
This means pages can have all the advantages of SSG pages, without worrying about outdated data.
Thankfully, Next.js makes using SSR and SSG incredibly simple. In fact, you don’t need to do anything! Pages are statically generated by default unless they contain the function getServerSideProps
, in which case they are SSR.
File-System Routing
Another great feature offered by Next.js is file-system routing, meaning URLs are determined entirely by the name of files and the directories they are nested within. To start, a directory called pages/
is placed within the project root or the src/
directory. Then pages and directories placed within this directory are mapped directly to URLs as shown below:
pages/index.js
→/
pages/blog/index.js
→/blog
pages/blog/first-post.js
→/blog/first-post
Furthermore, Next.js directly supports dynamic routes (routes with changing segments) by including brackets within the naming of these routes:
pages/blog/[slug].js
→/blog/:slug
(/blog/hello-world
)pages/[username]/settings.js
→/:username/settings
(/foo/settings
)pages/post/[...all].js
→/post/*
(/post/2020/id/title
)
The parameters included within the brackets can be accessed from the router in components and data fetching methods such as getStaticProps
:
// In the page pages/post/[pid].jsimport { useRouter } from 'next/router';
const Post = () => {
const router = useRouter();
const { pid } = router.query;
return (
<p>Post: {pid}</p>
);
};
export default Post;
API Routes
A popular software stack when working with React includes Node and Express for the backend. However, these technologies are serverful, meaning a server is required to be running the backend code 24/7 365 days a year. Yet one of the largest advantages of Next.js is its ability to be run serverless, meaning the backend code is only run when needed. Next.js includes API routes that support serverless by default and are incredibly easy to create.
export default function handler(req, res) {
res.statusCode = 200; return res.json({
name: 'John Doe'
});
}
These API routes also use file-system routing and support dynamic routes, which is a considerable improvement over the project structure used by Express apps. Overall, the inclusion of API routes within Next.js offers a considerable benefit to projects by enabling backend code to be included within the same code-base as the frontend.
In Conclusion
I think it's time for Create React App to stop being the (unofficial) default framework for creating React apps. I’m not saying it should be the end of Create React App, or that Next.js is perfect and should be the default, but there should be a higher focus on choosing the correct framework for React apps in 2020.
In the past, there was significant overhead in creating SSR apps over the ease of Create React App’s SPAs, yet the barrier of entry for SSR has dropped immensely with the creation of SSR starters like Next.js.
In my opinion, as long as Next.js continues to offer the same ease of setup as Create React App, it should be the preferred starter framework for React. As all Next.js apps can be written the same (with a few differences such as file-system routing) as Create React App, while having the advantage of default SSG of pages and many opt-in features, there is no reason to choose Create React App.
If you are interested in seeing more examples of projects using Next.js, check out the following projects: