As we step into 2024, React.js stands tall as the most popular tool for modern web development. With its massive user base, React has created a diverse ecosystem of libraries and frameworks built upon its solid foundation. In this first post of a two-part series, we will examine three of the most popular and innovative methods of working with React, discussing their advantages, disadvantages, and trade-offs. The upcoming post will cover three additional methods, continuing our exploration of cutting-edge approaches in the React ecosystem.
While huge ecosystems provide a lot of opportunities and solutions to common problems, the abundance can sometimes be overwhelming. In order to pick the right tools for the job, we usually need to take many things into consideration. Project requirements, security, performance, developer experience, and infrastructure costs are just some of them.
What is the process for setting up new React projects?
This is the first question that comes up when starting a new project. In the early days of React, this involved manually configuring bundlers, transpilers, task runners, and various other components. The process of setting up a project properly was often more complex than the apps we were building. Recognizing this challenge, the React team developed 'create-react-app', a comprehensive tool that streamlined this setup process. This innovation significantly reduced the entry barrier, prompting a wider range of developers to experiment with and learn React, marking an important moment in the evolution of React and web development.
Today, the landscape has evolved further with the emergence of new tools that take developer experience to the next level. Let’s explore some of those tools to understand their impact on modern React project initialization.
Vite
Vite is a modern bundling tool for the web. It supports nearly every significant JavaScript/TypeScript framework. Out of the box, it gives us a lightning-fast dev server with hot module replacement (HMR), TypeScript, JSX, CSS support, and much more, all while being fully customisable. It's considered one of the favorite tools among the web development community.
A crucial aspect to remember when using Vite for React projects is that it's primarily utilized for developing Single Page Applications (SPA).
SPA is a web application that loads one HTML page and dynamically updates that page based on the user's interaction with the app. SPAs leverage JavaScript to create smooth, fast, and desktop-like applications without requiring page reloads.
The approach can be considered a traditional way of building React applications. This method, while having its own pros and cons, has been widely adopted and practiced over the years. Let’s explore some of the tradeoffs of this approach.
Pros
- Flexibility - Freedom to choose our own tools for everything from routing to state management to data fetching, etc.
- Fast user experience - SPAs are known for their quick, responsive user interface, providing immediate feedback to users.
- Simple infrastructure - easy to build, deploy, and scale on any infrastructure, as we’re serving only static files.
- Offline support - implementing offline support is often not an easy task, but with SPAs, it’s easy to store information on the user’s device, allowing them to use the app even when there’s no internet connection.
Cons
- Poor SEO - since SPAs cannot run on servers, they cannot generate dynamic meta tags, which is very bad for visibility on search engines.
- Prone to UI/UX issues - such as content layout shifts, having too many spinners, and data flashes as a result of client-side data loading.
- Network waterfall - fetching multiple resources at the same time can lead to delays in rendering, as each request may need to wait for the completion of the previous one, potentially impacting the overall user experience.
- Setup complexity - setting up everything correctly for React SPA could be a challenge if you're uncertain about the optimal choices. Here are some important recommendations for setting up React SPA:
1. Use Vite as your bundling tool for all the reasons we mentioned previously.
2. For routing, there’s a proven, battle tested solution in the form of React Router. You could also explore TanStack Router. It’s recently gotten to version 1, and it has a lot of interesting features, with great support for TypeScript and URL query parameter management. It’s definitely worth a try.
3. Context APIs and core hooks like useState and useReducer can take you really far when it comes to managing your client state. For more complex use cases, Zustand is a pretty safe choice.
4. TanStack Query, formerly known as React Query, is an amazing choice for asynchronous state management, like data fetching. This is one of the most influential libraries in the history of React. It greatly simplifies the way we’re fetching and mutating server data.
Later in the article, we will explore some other interesting trends and recommendations that apply to all React-based projects, not only SPAs, so keep reading!
Overall, building React SPAs with Vite is a good choice when there’s no need for server runtime in our Fontend project. It gives us a lot of flexibility and is very simple to build and deploy. That makes them ideal for dashboard-like projects with high interactivity that don't require exceptional SEO.
Next.js
Next.js is a full stack web framework based on React. It’s one of the first server side rendering (SSR) frameworks in React’s ecosystem and definitely the most popular one. It popularized many modern paradigms in React and the web in general.
Next is created and maintained by Vercel, along with many experienced members of the original React core team. It has become the go-to choice for building React applications, establishing itself as one of the industry standard frameworks for web. Over the past decade, it introduced a lot of innovation, especially in the past few years with its implementation of React Server Components (RSC), which brought a lot of interesting features for React developers.
Pros
- Server runtime - Next.js runs on server, which is great for fetching data and sending full HTML responses to browsers. With that, we avoid some of the common drawbacks of SPAs we mentioned, like network waterfalls, content layout shifts, inability to make good SEO, etc.
- Static Site Generation (SSG) - is one of the very common techniques we utilize when our data does not change too often. With it, we’re able to pre-render React pages and serve them as pure files. This causes significant performance improvements in certain cases. Next.js does this very well.
- Incremental static regeneration (ISR) - SSG is amazing, but every time our data changes, we have to rebuild and redeploy our project. That’s where ISR shines. With it, we can define how long our statically generated page will last. After it expires, Next.js will regenerate it in the background with the current state of our data. We can also choose to manually invalidate the ISR cache when needed.
- Image and font optimization - built-in image optimization that automatically optimizes and delivers images in various formats and sizes for efficient loading. Additionally, Next.js supports font optimization, making sure that only the necessary font subsets are loaded. This significantly improves performance and user experience, reducing page load times and enhancing overall site speed.
- App directory - with the new app directory router, Next significantly improves it’s original file based routing. It introduces a lot of new features, like route layout, loading and error components, nested layouts, ability to colocate all route related file with that route and much more…
- React Server Components (RSC) - This is a big one. A huge paradigm shift happened in React world, and everyone's been talking about recently. To cover all of it is beyond scope of this article, but TLDR is that running React on a server without client side JavaScript is the new default. This is a pretty big change for a tool that was originally created to replace the need for servers in frontend development.
With that introduced, Next made it possible for our React components to do anything we would traditionally do on the server. Like query a database, or read from a file, or proxy an HTTP request, and much more... We can still, of course, use our standard client side components, but it’s opt in, we have to flag them with 'use client' on top of the file.
This makes the future of React and web development very interesting, and it will be fascinating to see what will come out of it. One very important thing to keep in mind is that RSC are still in an experimental state, even though Vercel might say otherwise. It’s important to carefully approach new technologies like these since they can have serious impact on performance and security.
Huge popularity and community - Next has the biggest community of all React based frameworks, since it’s the first one that nailed many of the features we were missing in React itself. With that, it gained the trust of many developers. Also, many big companies use it at scale and are achieving great results.
Cons
- Infrastructure complexity - since Next.js primarily runs on a server, that adds significant complexity to the server infrastructure compared to SPAs. We need to handle tasks like managing, scaling, and monitoring those servers. This means more effort is needed to ensure everything runs smoothly and efficiently. Handling these aspects becomes crucial to keeping the application performing well, especially when it scales or experiences increased traffic. This is definitely worth and required for many types of projects, though.
- Two different architectures - right now there are two primary ways of working with Next. Pages and App Directory. Pages is considered old, stable, and reliable way of running things. While App Directory is new modern way of building with Next, it has a lot of advantages compared to “Pages,” the most notable being nested routing and React Server Component (RSC). The transition between these two models hasn’t been the smoothest since it requires switch to completely new mental model and lot of new APIs that are still considered unstable.
- Too much abstraction and complexity - with new app directory and RSC, it seems that lot of things are abstracted away from developers. For example, caching, complex routing, build process, patching global fetch (which they realized was a bad idea and are moving away from), etc. This gives us a feeling of not being in control of many important pieces of our tech stack.
- Not so simple outside of Vercel - the easiest way of running Next.js is with Vercel. Since developing the framework, they have built an amazing cloud infrastructure for running it. By utilizing this, much of the previously mentioned infrastructure complexity is eliminated, as they manage all of it on our behalf. We won't dive deep into the technical details of Vercel’s cloud, but it’s important to know that it’s based on serverless functions. The environment of serverless functions is drastically different from the standard long-running process environments we’re traditionally used to.
But what happens if we want to run it outside of Vercel for whatever reason? Well, if we wanted to follow a similar serverless model and have all the features of Next, like ISR, image optimization, streaming, etc., it would be very difficult to set up all of this ourselves. Thankfully, there is an open source project called OpenNext that lets us do all of that in a fairly simple manner on AWS infrastructure.
Another approach to deploying Next would be the traditional way of dealing with Node applications. Something like EC2 on the AWS cloud works just fine in most cases. The only issue with it is that our approach may not align with Vercel's vision for Next.js, which means that if we're not cautious, something could malfunction or become incompatible in the future.
Next.js is a remarkable framework that has significantly influenced React's history and development. It is versatile and fits well with most project types. Currently, Next.js is undergoing a major transition towards a new architecture, known as the "app router with RSC." While this architecture may not be completely ready yet, it holds great potential. Therefore, it's important to have confidence in its evolution and monitor its progress closely, as its future prospects look very promising.
Remix
Remix, like Next.js, is a full-stack web framework. The primary focus of Remix is on web standards and a fast, slick, and resilient user experience. It’s made by the team behind React Router, and it’s recently been acquired by Shopify.
Remix introduced exciting new features based on the existing web standards and APIs, boosted by the interactive capabilities of React. Its innovation has served as inspiration for various features in other frameworks like Next, SvelteKit, SolidStart, and more. Let’s explore some of those features.
Pros
- Server runtime - similar to Nextjs, Remix runs on server - which is great for fetching data there and sending full HTML responses to the browsers. With that, we avoid some of the common drawbacks of SPAs we mentioned, like network waterfalls, content layout shifts, inability to make good SEO, etc.
- Progressive enhancement - since it heavily leans on web standards, most things in Remix just work even without the client side JavaScript. That’s possible because, while our JavaScript is not loaded, Remix is smart enough to fallback to native browser APIs. For example, the Link component is just basic HTML anchor tag until JavaScript loads, but after that, it will become more interactive and handle links in the same way SPAs do, creating instant feedback without full page reloads.
- Rich features - Out of the box, it comes with many features like file based routing, nested layouts, SSR, data fetching, streaming, async state management, etc. Meaning that we don’t need to worry about setting up those things ourselves; they're already there.
- Great documentation - Remix has amazing documentation with a lot of practical examples on how to tackle real world problems we face every day as web developers. There’s also a collection of starter projects they call “stacks”. At the time of writing this article, there are three official stacks that are very well maintained and documented. They enable us to immediately start our project without having to setup all the common things like prettier, eslint, styling, testing, databases, infrastructure, CI/CD, etc. All of that and much more is included.
- Infrastructure flexibility - Remix can be easily deployed to any JavaScript runtime and infrastructure provider, which is very well documented.
- Vite support - remember Vite from a few sections ago, and how we mentioned it’s mostly used for SPAs? Well, recently, Remix supports it! We can now utilize all of the Vite’s features and its rich ecosystem.
Cons
- Infrastructure complexity - Same as Next.js, running Remix on a server adds significant server infrastructure complexity compared to SPAs.
- Route/data coupling - connecting routes with data can be a bit tricky since each route has to handle loading data from all its components. This becomes challenging with larger component trees, especially when some deeply nested components want to fetch their own data or when we aim to reuse components that handle their own data fetching. However, there is a way around this, as Kent C Dodds describes in his article “Full Stack Components”.
- No RSC support - One possible way around route/data coupling would be to use RSC (React Server Components). Unfortunately, at the time of writing this article, Remix does not support RSC, but it’s on their roadmap, which is a good sign for the future.
- No middleware support - at the time of writing, there are no official support middlewares, which can be very handy when dealing with things like authentication or logging.
- Not as popular as Next.js - smaller community; not as many resources outside of the official documentation and content created by Remix team.
In general, Remix is well-suited for projects of all sizes, domains, and complexities, offering out-of-the-box features, robust documentation, deployment flexibility, and, most importantly, focusing on empowering developers to create great user experiences.
Summary
React has reached heights that no other tool for building user interfaces has. We’re entering a new era of React with RSC, and we’re being enabled to do a lot more than before. I would argue that an average React developer will eventually become a full-stack developer. But with that comes a great deal of responsibility. Learning about performance, security, infrastructure, and software architecture in general has never been more valuable, because with these new models in React, we will need to apply that knowledge.
Now that we have learned how to get started with the React project, let’s figure out how to make it safe and maintainable on a language level. Head over to 'How to React in 2024, Part 2' where we'll uncover more innovative methods, provide insights, and explore further how React is shaping the future of web development.