Things you forgot (or never knew) because of React
Part 1: an intro about music, defaults, and bubbles
Like a lot of people, there was a time when the only music I listened to was whatever was played on my local radio station. (A lot of people over 30 or so, anyway. If this doesn’t sound familiar to you yet, just stick with me for a minute here.) At the time, I was happy with that. It seemed like all I needed.
Looking back, I realize: I naively trusted that anything good inevitably became popular—and therefore, anything worth knowing would eventually come my way on its own.
Eventually, though, other music began to take root in my life. Through new friends and the internet, I became acquainted with new artists, further and further from the things I liked before—or, at least, thought I liked.
This music was different. I wasn’t in love with it one week and sick of it the next. Listening to it wasn’t part of an endless cycle.
If anything, it was the opposite; it was music I actually liked and appreciated more the more I listened to it. There was depth to it. Sure, it didn’t have the loud distorted guitars, punch-line lyrics, or sugar-coated melodies I’d enjoyed up until that point. But to my surprise, that actually somehow made it better, not worse.
That’s when I began to realize: maybe I was never really as satisfied as I thought I was.
Maybe my bliss was, in fact, predicated on ignorance.
Finding richness beyond the defaults
I suspect you can probably relate to that story, even if it’s not with music specifically.
Most likely, you now count a food or drink you didn’t once like among your favorites. Or, maybe you were surprised to find a movie, book, game, podcast, influencer, or hobby you didn’t expect to like resonated with you.
The details aren’t important; all I’m getting at is:
You’ve probably experienced finding something great beyond the periphery of popular defaults.
Not to sound like a frontend version of a snobby hipster. That’s not my intention. If your idea of a good time is Bud Lites at Olive Garden: cool, pass the breadsticks.
But what I am trying to do is: gently share the idea that maybe you’re shutting yourself off to something great, without even realizing it.
Maybe this whole concept—finding better things beyond familiar boundaries—applies to our tools and workflows just as much as it does any other area of life.
And maybe—just maybe— your current satisfaction comes, at least a little bit, from simply not knowing what you’re missing.
Completing the analogy, and acknowledging its shortcomings
And on that note, this is where our analogy begins to fall short.
Assuming we were only talking about personal preferences, I’d never write a blog post arguing about what you like, or trying to change your mind. (Not at this age, anyway.) Who cares? If you enjoy it, have fun.
But unlike music or other subjective things meant for our own enjoyment, our choice of frontend tools has empirical, measurable effects on others.
That decision carries a real responsibility. It’s not just about what we like. When it comes to development—unless we’re building things purely for ourselves, anyway—our enjoyment is secondary; the user’s experience is what matters most.
If you love your tools, that’s wonderful. I hope you do. But that’s a side quest at best, and a potentially harmful distraction at worst. Developer experience (DX) shouldn’t ever supersede user experience (UX).
So forgive me for choosing a flimsy metaphor. You can keep listening to the same music for the rest of your life, if you want to. I support that. But we have very valid and important reasons to push beyond the comfort of our existing preferences when it comes to the tools we use.
The React bubble
The idea that React lags behind its peers might be new to you. Like many, you might still consider React the modern standard in frontend. So let’s quickly poke at that bubble, in this one last section before we get into the titular list.
This, from Alex Russell, via Mastodon, is what started me writing this post:
Someone asked me today if there was a case for using React in a new app that doesn’t need to support IE.
I could not come up with a single reason…
It’s astonishing how antiquated React is.
Alex mentions React’s lack of support for web components in that thread. That feature has been glaringly missing from React for years. And yes, it’s “on the roadmap.” As of this writing, though, there’s no firm commitment to either an implementation or an expected ship date.
Meanwhile, pretty much all of React’s contemporaries—any framework or technology you might choose instead of React—already have that story shipped and in production.
Web components are one thing. But they’re far from the only item on the list of “stuff everything else does already and/or better.” (We’ll cover several others below.)
React benefitted mightily from being early to the framework game; it set the standard. But that’s come with severe drawbacks in agility and adaptability. Every decision React’s made since its inception circa 2013 is another layer of tech debt—one that its newer contemporaries aren’t constrained by.
React is ‘13 tech designed to ‘08 constraints. Nothing about it is innovative in 2023; in fact, it’s the slowest way to get functional-reactive frontend programming in the modern era…
React has aged, and how I don’t think most people realize how much or how poorly. So to put the quote above another way (and tie it back to our intro about music):
React was designed seven Taylor Swift albums ago, for a world where John Mayer and Jennifer Aniston were still dating.
(Seven new Taylor Swift albums ago, that is. That doesn’t even count the Taylor’s Version releases.)
So if you’re one of the many developers whose whole world has been React for the past few years, there might be things you’ve forgotten—or never knew at all—because you’ve been using React for so long.
As fast as modern frontend moves, we seem to be very slow in realizing the world which crowned React king, in many ways, no longer exists. (If it ever did; not many organizations had anything resembling Facebook’s specific set of problems to begin with.)
I realize calling React “legacy software” is controversial, but I think it’s fair; it’s comparatively complicated, relatively old, contains a lot of rules and gotchas, beginners are often afraid of it, and the architectural decisions it’s built on top of have long since become an impediment to its ability to iterate.
If I haven’t completely alienated you yet by this point (with some combination of quasi-elitism, rambling preamble, and overuse of parenthetical interjections), I’d like to share some things you might have missed if your head’s been entirely in the React world for a while, in the hopes of introducing you to some tunes you might be surprised to find are better than what’s on your current playlist.
Part 2: things you forgot (or never knew) because of React
Your ecosystem doesn’t need to be massive anymore (because it can share now)
I’ve touched on this in other posts, but any time an “unproven” framework’s name comes up as a potential tool for a dev project, the first question anybody seems to care about is: how big is the ecosystem?
You might have even had that thought as soon as you read the premise of this post. Move from React to another framework? Are any of them big enough yet?
Why do we have this obsession with ecosystem size?
Sure, we want to be certain this framework won’t just vanish on us, or stop being maintained in a few years. That’s perfectly valid. And yes, we wouldn’t bet the farm on something too new or unproven. But Vue, Svelte, Preact, Solid, Astro, and others are all far past that point, well-supported and well-maintained. So it clearly isn’t just that.
So what is the sticking point? I have a theory:
We’ve been trained that packages need to be built specifically for our framework.
You could reasonably argue that mindset started with jQuery, but I think React accelerated it.
React trained us that things need to be built specifically for a certain framework. But that’s not very true anymore, and it arguably never should have been.
Sure, other frontend frameworks have their own rules and conventions about state and architecture. You can step on figurative rakes in their yards, too. And yes, there will always be things that are, and need to be, built specifically to work with Svelte or Vue or whatever else.
But crucially—and I want to emphasize this as strongly as possible:
No other modern frontend framework is as stubbornly incompatible with the platform as React is.
You often don’t need a specialized package or library tailor-built for your thing, because your thing probably already works with the platform, and therefore, everything else that’s already out there.
Where the frameworks fall short, it’s likely the platform already has the thing you need. (Form submission, for example; always a pain point in React, now made infinitely easier by two-way data binding and just using the conventions browsers give to us.)
And worst-case, it’s probably a lot easier to build whatever thing you need than it was in React. (It shouldn’t take very much comparing of
useState with other frameworks’ versions to see that.)
Being newer is often considered a disadvantage by conservative-minded developers who are wary to test the waters with something that hasn’t been thoroughly vetted in every which way possible. But it’s important to remember that being new is also an advantage, because there’s less tech debt and old browser support to worry about—and new things are free to iterate further on existing good ideas and more modern browser features.
React hooks are actually kind of outdated
Hooks are the newest evolution of React, replacing class components.
Credit where it’s due: hooks were a massive shift in the frontend space. They revolutionized how we composed logic and state in our applications. Hooks are undeniably great, and pretty much every framework has coalesced around a hooks-like model for managing state.
But React hooks aren’t new anymore. (In fact, stable React with hooks is almost exactly the same age as my kid, and he’s starting pre-k in a couple of weeks.)
Hooks are no longer a competitive advantage, or even a notable feature; they’re the baseline. They’re just the way we do things.
Every other framework not only has its own implementation of hooks, but notably: every one of them is faster, smarter, easier to write, or a combination of all three.
Preact’s Signals warrant mention here; so do Svelte’s dead-simple stores. Solid, too, has Signals. Even Vue 3’s composition API, which is pretty directly inspired by hooks, has some key advantages over the React implementation.
Hooks are an excellent pattern, and React deserves credit for popularizing it. But pretty much every other framework does hooks better, with fewer rules, and with less boilerplate.
If you’re unfamiliar with the concept of Signals: it’s a crude oversimplification, but you could think of them as the next, better evolution of reactive state; an update to hooks, with better defaults around what causes re-renders, to only re-render the nodes that need to be re-rendered, instead of entire components.
You don’t need to micro-manage rendering anymore
I have a confession to make: I’m still not exactly sure what the difference between
useCallback is—or when you should and shouldn’t use them—even though I literally read multiple articles on that exact topic earlier today. (No joke.)
I have a second confession: it’s still not intuitive to me what should and shouldn’t go into the
useEffect dependency array, or why. I feel like every time I write a
useEffect call, I spend like 15 minutes refactoring my code to be in a shape the linter likes, even when I’m 99% certain it’s actually fine and it’s not going to suck my app into an infinite abyss.
I’m betting if you use React, you can probably relate to those confessions. And maybe you’ve even just accepted that confusion and ambiguity as normal. But if so, you should know:
We haven’t had to do this kind of rendering cycle micromanagement in other frameworks for years.
These days, frameworks are smart enough to handle this kind of thing without you needing to hold their hand and explain what they should do.
They already know not to waste precious resources re-rendering when there’s no real need. They’re intelligent enough to only update values, and not constantly reevaluate things that don’t need it.
…Most of the time, anyway. They’re not perfect. But they are much better than React at knowing what to do, and doing it in a performant way by default.
You might need to optimize some things in other frameworks, too. They’re not perfect. But by the time you do, you’re way, way past the point where you would’ve needed to in React.
Nobody else is afraid of their framework’s version of
When you want a component to just do something when it enters the DOM—and/or when you want it to recalculate something dynamically, based on some other data or variable(s)—just about every other framework has a better way than
I don’t think I need to harp too much on this here, because even within React communities,
useEffect is considered notoriously hazardous, and often even avoided altogether. But trust me: no other non-React-based frontend framework has people so afraid to use such a normal, useful feature, and nowhere else are there such obtuse rules around it.
Nobody else is looking at third-party packages just to do something when a component is mounted without shooting themselves in the foot.
Scaling isn’t really a frontend concern anymore
This is the other question people immediately ask when a new(er than React) framework comes up: does it scale? But I believe that question might be a bit outdated.
It’s worth remembering: the world that gave us React had a different set of problems.
That’s because you had to write your own selectors for each and every element and DOM node you might want to interact with, and you had to come up with your own manual way of tracking and syncing state. That usually involved writing to and retrieving from the DOM, which was messy, error-prone, and most importantly, slow. (That’s where the virtual DOM came in, but even that has been pretty thoroughly outdated for years.)
Writing modular code back then was difficult to impossible, and JS files often ballooned to hundreds of lines, if not thousands. If multiple authors were working on the same project, they’d often reinvent, repeat, or even override each other’s code (partly because code often went into a shared global namespace, which made collisions even more likely). And the bigger or more complex your app (Facebook), the worse the problem was.
It’s important to remember: that’s our baseline for “does it scale?” as it relates to frontend. Does it stay reasonably maintainable even if my app grows exponentially?
The worry that a frontend framework might not scale is as old as jQuery, and should be considered just as antiquated in relation to modern web development.
React solved many of these problems, yes. But it didn’t do so by being a marvel of modern engineering, so much as simply coming up with a good way to manage and share state, make data reactive, abstract complexity, and enable developers to share the same programming patterns without conflicts, namespace collisions, or overrides.
React wasn’t the best, only, or even first solution to frontend scalability; it’s just one of many possible versions of the same paradigm.
(It also happens to be among the oldest.)
How do I know this? Because a plethora of benchmark tests have been run, with publicly available results, comparing the performance of React to every other frontend framework at scale. (I’m not linking to any here, because they’re readily available online.) They all confirm that just about every other option in the frontend space does as well or better than React—and in many cases, dramatically better.
Here I’m referring to scaling in the general sense; making sure complexity stays minimal, and doesn’t grow linearly as the app increases in size. Certainly, some frameworks will scale much better or worse than others in terms of, say, building static HTML from Markdown files, or other more specialized tasks.
Server-side rendering isn’t special anymore
There was a time, several years ago, when React was pretty much the only game in town when it came to server-rendered content (mainly via Next JS). People were rightly excited for the idea that React could be rendered on a server as HTML, instead of on the client as a Single-Page App (SPA). The speed and SEO gains were impossible to ignore, and initially, it took other frameworks a bit to catch up.
However, as is a theme with these things in general, and with this post in particular: the first to iterate is rarely the best.
SvelteKit is server-rendered by default, without you needing to do anything, and offers fine-grained control over its rendering patterns. Nuxt, Vue’s meta-framework, was earlier to the game (being obviously inspired by Next).
Fresh (Deno’s frontend framework) is entirely server-rendered, except for what you designate as an “island” (client-rendered); anything else just ships as static HTML. Fresh also uses Preact (which, again, is even faster than React, and which has Signals, a much more performant and ergonomic version of
useState and the reactivity model).
Astro has server-rendering, and just lets you server-render whatever flavor of components you want. It can render other frameworks’ components just fine, and has even been noted as a major performance upgrade from Next, in some cases.
Point is: way back when, React was one of few frameworks that had the concept of rendering client view framework components on a server. But now, server rendering is table stakes. A lot of newer frameworks don’t just have the option to render on the server; they do it by default.
PHP is back, baby.
Two-way data binding isn’t hard and it isn’t a bad idea
Or, a better title for this section might be: forms just aren’t supposed to be that hard.
I think it’s important to remember that React was created by Facebook, in order to solve Facebook’s unique set of problems.
One of React’s strongest opinions—that data should flow only one way (top down)—is a good example of how the engineering challenges of Facebook in the early 2010s indelibly shaped React’s architecture.
For some time, it seemed like one-way data flow was considered a best practice. These days, though, we’ve mostly figured out solutions to the pitfalls of two-way data binding, and found that in many cases, it’s actually much more convenient.
Working with forms in React is notoriously cumbersome because every user keystroke is a two-step process: get the value from the input; then set the state to match it (which in turn needlessly re-renders the input, to contain the exact value it already did, but synced up with React state). Sure, it’s usually too fast to notice, but it’s a lot of extra work.
This is why there’s a proliferation of form libraries for React; making a form in React is notoriously one of the most difficult things to do with the framework. In fact, I can genuinely say I have no knowledge of any other tool that makes working with forms so infamously unwieldy.
Forms are a standard web platform feature, and many frameworks that have come since have opted to trust and use the platform, rather than push so obstinately against it.
Even if you forget about the server side of things, though, and deal with forms purely on the frontend: Svelte, Vue, and many others, don’t have the same data binding and syncing issues. You can just bind state in such a way that it updates automatically from both ends. If the state changes, the DOM updates; if the DOM changes, the state updates.
This way, you don’t have to do the multi-step dance. If you just want to capture, say, the value of a text box, you do two-way data binding. Then, when the user types into the field, the data updates automatically, and you can get it whenever the time is right with no further steps. If in the meantime you need to do something like set a value or clear the field, that’s also a simple one-liner.
Two-way data binding lets you keep data and the DOM in sync without the need to constantly make sure one is keeping up with the other.
Could you get in trouble using these? For sure. But I find that dogmatic ideals of best practices get in the way as much or more than they help. One-way data flow is a prime example.
Styling is easy, actually
If you work mostly in React, it’s quite possible you’ve gone through two, three, or more iterations of handling styles in your frontend components.
You might have imported .css files straight into JSX components, or used CSS Modules, Styled Components, and/or Tailwind (probably with either the
tailwind-merge packages—or maybe even both, plus some extra Tailwind add-ons). And those are just the most popular options.
Tailwind is its own rabbit hole (and its own frontend framework I’m not particularly a fan of; I consider it cutting against the grain of the platform in exchange for short-term gains that eventually compound into long-term losses). But in any case, these styling solutions exist and see significant adoption at least partly because React’s had a vacuum in place of first-party styling options for as long as it’s been around.
You might not realize styling is a solved problem in several other frameworks.
In particular, Vue and Svelte both have their own component styling story. They both have component-level scoping (Vue’s is opt-in; Svelte’s is opt-out). They both work wonderfully with vanilla CSS, if that’s the way you want to go. But both of them—along with every other frontend framework—are still compatible with CSS modules, Tailwind, Sass, or whatever else you like to use.
But most importantly: all the supposed problems with CSS—whether you actually consider them problems or not—are fully addressed by the built-in style handling. You don’t need a mess of packages and configs nearly as much anywhere else, because scoped CSS solves just about every issue you could possibly imagine.
Seriously; read through any list of reasons CSS is supposedly bad (it’s not, but people who are bad at it like to say that). Just about any critique you could possibly have of CSS is solved by scoped styling, and multiple non-React frameworks just come with it already built in.
Frameworks aren’t as hard to learn anymore
I theorize developers mainly trained on React think back to how difficult it was to learn, and assess the learning curve of other frameworks similarly. And that’s probably part of what keeps us from trying new things; it seems really hard, because it sure was the first time.
All the ins and outs of state management, props, nesting, component lifecycles, hooks, and of course, how to write JSX…it’s a lot. Even the most ardent React fans would likely concede it’s not the easiest thing for beginners to pick up quickly. (Anyone who says otherwise has probably forgotten what it was like to be a beginner.)
If you can relate, I have good news:
There’s no comparable tool as difficult to learn as React is. But once you know one framework, you have a huge head start on all the others.
I compare this to learning your second musical instrument (not just to tie this back to music again). The first time you learn to play, you’re learning everything about music, on top of learning your specific instrument, and how to get it to make the sounds you want. But when you learn your second instrument, you get to skip so much. All the concepts are familiar. You understand music. All you need to do is transfer your existing knowledge and muscle memory into a slightly different shape.
Frontend is similar: every frontend framework has components; they’re all compatible with TypeScript; they all have the concept of props, children, and reactive state. These are things we’ve generally agreed we like and are good. They just have different takes on implementation.
And speaking of which: while React undoubtedly helped to proliferate these ideas, it would be silly to consider React the ideal implementation of them.
Great things are created through iteration, and for the most part, other choices in the frontend space that came later have the distinct advantage of iterating on top of the core ideas of React.
This means React is a bit like a git branch that’s fallen well behind
main. You might not realize it, if React is the star your galaxy orbits around, but…well, frontend has moved on. The ecosystem has taken those ideas and run with them to make things that are even better.
We have no shortage of more performant, less complex, less difficult-to-learn options available to us now. And if you know React already, none of them will be very hard to learn as well.
The following two sections were not included in the original draft of this post, and were added on August 15th, 2023, to address feedback from the original version.
Other options aren’t just “new and shiny” (and nobody cares about that anyway)
A common refrain I hear when discussing pieces such as this one goes something like this:
…But how true is that, actually? Are we sure every JS dev in the world is rushing out to rewrite their entire tech stack the moment something new arrives? Or is that just what it feels like, fed by an endless online hype cycle?
I think it seems a lot more true than it is in reality, because early adopters of any given technology tend to be the ones getting all the attention; they’re the ones writing all the blog posts and making all the videos, which in turn get shared and talked about in a way that makes it seem like there’s a lot more of this kind of behavior going on than there actually is. (After all, if it was even half true, React’s market share would be a fraction of what it is currently.)
Most of the frontend devs I’ve met stick with what they know, just like any other kind of developer. I think it’s just a little less costly for us to dabble in new things, and so we do.
That’s all beside the point, though: this view underestimates the maturity of the other options available in the field.
Vue has been around as long as React, and its current version (v3) launched almost three years ago. The modern version of Svelte was launched only two months after React hooks did (did you think React hooks was new and shiny two months ago?), and SvelteKit hit 1.0 almost a full year ago. Preact is on version 10. Solid’s been 1.0+ for over two years. Astro hit 1.0 a year ago. Qwik and Fresh are the newest entries I’ll mention here, but they’re both at 1.0 as of earlier this year.
So if some of those entries are still a little too new for you, that’s fine. I get that. But reducing any consideration of React alternatives to a short attention span is a fallacious glossing-over of the maturity and depth available in the field.
React is WAY behind in performance
I won’t harp too much on this here, because much has already been written on the topic (some in this other post I wrote, much in other places easily discoverable online). But suffice to say: where React is slower, it may seem like it’s by a negligible amount (especially if you’re privileged enough to be using very new/premium hardware, on a very fast internet connection). But the difference is far from trivial.
React falls behind in terms of performance (and here I mean both bundle size and execution speed) by factors of 2 or more in many cases. (The bundle itself can be by 10x or more.) The latest run of the JS web frameworks benchmark places React’s performance, on average, at almost 50% slower than Solid, 25% slower than Vue, 40% slower than Svelte, and 35% slower than Preact. (Other frameworks weren’t available in this particular test.)
That comparison was using only React hooks; no
useTransition hook, and no additional libraries like Redux.
There are other studies, too. I’m sure the numbers are a bit different everywhere, and I’m sure there’s always nuance to their findings. But:
Pretty much no matter what results you consult, React will be larger and slower than just about anything that’s come since.
Anecdotally: I can tell, as a user, when the form I’m filling out on my Android uses React. There’s a slowness to it, and its loop of updating the DOM to match the state to match the DOM, that I don’t notice unless it’s React.
But forget anecdotes; we just saw data. And I know those numbers might not sound big, and that performance is one of many considerations when choosing your tech stack. “It all depends.”
But let’s realize that a 25–50% improvement is massive in terms of performance gains. Making the frontend load up to twice as fast for users is far, far beyond the boundaries of a marginal nice-to-have; we’re talking numbers that add up to a colossal difference at scale.
Any engineer who eliminates a chunk of loading or processing that sizable has it in a prominent place on their résumé. It’s a big deal.
The performance difference between React and the rest of the field isn’t minimal; it’s minimized.
Part 3: the other stuff you should try
You probably started wondering a few dozen paragraphs ago: if React is so antiquated, what’s the alternative?
I’m going to cover several here, and mention their use cases as well. One of the issues with React is that it’s long tried to be everything for everyone, and useful though a React-shaped tool might be, I think maybe two or three different tools could be better than one Swiss army knife.
Two quick notes before we dive in, though:
I list several options here, for the sake of covering all the other modern frameworks I mentioned above. I don’t expect anyone to learn about—let alone use—all of them. If you have to pick one, go with Svelte, or maybe Vue. But in any case, know that I’m only listing them all for the sake of thoroughness.
I didn’t list all the options here. There are others.
I omitted Ember and Angular, for example, because they’re both older than React, and don’t generally tend to outperform React significantly, if at all, in benchmark tests (sorry, Mel).
I also omitted the lightweight options like Alpine and Petite Vue, since those are more replacements for jQuery than React, and shine where you might not need something as heavy-handed as a framework.
Finally, I also omitted exceptionally good tools in and around this category, like Eleventy, since it’s more of a pure static site generator than a framework. (Still worth a look if you’re using Gatsby, however.)
All that said: here’s your Discover Weekly.
Svelte (my personal pick)
Ladies and gentlemen of the class of 2023: use Svelte.
If I could offer you only one tip for the future, Svelte would be it.
Joking aside: if I were to pick one thing from this list to recommend over React, it would be Svelte. I’ve long maintained that “Svelte is React, but without the bullshit,” as I originally quipped on Twitter back in 2019 (RIP), and if anything, that’s only grown truer over time.
Svelte is delightfully simple to use, comparatively easy to learn (especially if you’re coming from the React world already; even the syntax is often similar), much, much more performant in just about all cases, and capable of anything React is and more. This site, and all my own side projects these days, are written in SvelteKit.
Svelte is fast; it’s comparable to the fastest options available. Its DX is phenomenal; it regularly appears at or near the top of most-loved frameworks in developer surveys.
Svelte hews as closely to the web platform as possible, so even though it’s incredibly powerful, its concepts will be largely familiar. Svelte also includes transitions, easings, CSS handling, component-scoped styles, and more niceties out of the box.
Although it feels and works like a framework, Svelte is, essentially, a small, elegant superset of HTML, with a delightfully simple syntax, which compiles to fast, minimal bundles.
Svelte’s own meta-framework, SvelteKit, is highly versatile and powerful, capable of static, server-rendered, deployment to the edge, and even mixing per-route. It hit version 1.0 at the end of 2022 and is very ready for production. (It’s also supported by Vercel, who make Next.js as well.)
Svelte is recommended if:
You want to rediscover the joy of frontend with (what I consider to be) the best all-around option, for the reasons above.
Anything you’re doing with React. Svelte can replace React itself, or SvelteKit is versatile enough to sub in for Next, Gatsby, and/or Remix (or even all at once).
Vue is possibly the closest option to React, and likely has the next-biggest ecosystem. It’s significantly more performant than React, however, and a bit more UI-focused.
In some ways, Vue is the smallest leap from React, especially now that it has a similar hooks-based approach in Vue 3. But Vue uses a templating language closer to default HTML than to JSX, which makes it much easier to write conditionals and loops in template files, without having to reach for workarounds like
map and ternaries.
Vue has a similar meta-framework to Next in Nuxt, which is well maintained and adding powerful new features all the time. Vue is also a bit more batteries-included than React, coming with things like scoped CSS handling and easy transitions/animations out of the box.
Vue is recommended if:
Community size/overall framework popularity is an important factor for you; you want something like React, but more batteries-included or HTML-like; you prefer your framework to be independent and not be owned by a large corporation.
React itself, or Nuxt can replace anything you might be using Next for.
Solid is what I would call React, but better. It looks almost (if not entirely) identical to React in many cases, but Solid is far, far more performant. It’s one of the fastest options available, in fact.
Solid essentially starts with React, and then rethinks it to eliminate complexity, performance issues, and a lot of boilerplate. Signals appear as a concept in Solid, which eliminate a great deal of the confusion and footguns around component rendering and lifecycles. It might even be fair to say Solid is React, if React was built in the modern era, on top of all the lessons we’ve learned since 2013.
Solid also offers its own meta-framework in SolidStart, though that is currently in beta. Solid itself is plenty mature enough to use, though, and boasts an impressive gallery of sponsors.
Solid is recommended if:
You generally like React (and JSX), but you just wish it was more modern, faster and/or easier; performance is an absolute top priority.
React and React DOM. SolidStart will likely be capable of replacing Next one day, but it’s still in beta as of this writing.
Fresh is a server-rendered frontend framework with islands architecture, built on Deno. It’s a bit younger than most of the rest of the items on this list, but it’s full of promise as a minimal-JS, island-based framework that can run on the edge—powered by Deno, no less, which means your server code is faster, more secure, TypeScript by default, and all the other benefits Deno brings over traditional Node (such as easier, first-party linting, testing, and code formatting settings).
Fresh uses Preact, so you know it’s fast, and won’t be difficult to pick up if you’re coming from React, either. And again: building on Deno feels great.
Fresh is recommended if:
Remix is probably the closest thing to Fresh in React-land.
Astro is a next-gen, highly performant static site generator that does more than static. Astro is one of the newest options on this list, but it’s already at a very stable 1.0 release and has garnered widespread praise and adoption.
Built mainly to be a new generation of SSG (hey, React fans: it supports JSX and MDX), Astro now also features dynamic, server-side capabilities as well. I’d definitely recommend it over, say, Gatsby, or for any content-heavy or static sites.
Astro is also compatible with whatever frontend framework you want to use, so if you prefer to template in React, Vue, Svelte, or others, you can!
Astro is recommended if:
Gatsby, or similar React-based content tools.
You probably already know about Preact if you live in React land, but it warrants mention here. It’s a much slimmer, much faster version of React. Although it began more-or-less as a drop-in replacement for React, it’s beginning to gain some superior features React doesn’t have (like Signals, which we’ve already mentioned).
Preact is recommended if:
You want to stick with React, essentially, but you just want it to be faster.
React. (Actually, it just adds a P to the beginning. The P stands for performance. I made all that up; don’t blame the Preact team for that.)
Qwik is recommended if:
React itself, allowing it to run very efficiently on the edge.
Web component libraries
I won’t go very deep on this one, because frankly, I’m not the guy for that. I don’t have the experience with either web components on their own, or web component frameworks, to speak well on the topic.
That said, there is a certain class of projects that could benefit from a web component framework/library like Lit, Stencil, Polymer, or others. Rather than generating “proprietary” components in a specific frontend framework, these libraries help you write actual web components, which are then portable to any web project.
In my opinion, most projects still benefit from using a frontend framework over pure web components—or, at the very least, both together. Maybe that will change in the future, but for now, I think the tradeoffs still tilt away from a pure web component approach in most cases.
Still, there are certainly use cases for which a purely web component-based approach ought to be considered. And for those projects, React is definitely overkill. The web component libraries mentioned above would be a much better fit.
Web component libraries are recommended if:
You need to reuse the same components in multiple environments; want to future-proof yourself against framework changes; or just prefer using the platform, and are prepared to deal with the tradeoffs of web component authoring.
Web components replace:
React, but maybe only partially, depending on your use case
This post is, admittedly, a lot like my post from last year, The self-fulfilling prophecy of React. It treads some of the same territory, and makes some of the same arguments (albeit hopefully in new ways or from new perspectives).
I didn’t set out to repeat myself, but clearly, I think about this stuff a lot—spurred no doubt by my professional shift to working with React full time around the time that post was published, by coincidence.
I’ve come to believe React’s popularity is, in no small part, because folks don’t look beyond it. It’s not the greatest, but most people aren’t looking for the greatest; they’re just looking for good enough. (We’re humans. There are a lot of personal, emotional, irrational reasons for our decisions, all of us, and that’s fine. We’re busy.)
It seems like we adopt technologies in leaps, rather than in a linear motion, at least in the world of frontend. Part of what caused everyone to jump on the React bandwagon was that everyone at the time was stuck on antiquated technology, and was looking for something better. We didn’t gradually advance to the new thing, in small steps (maybe because that wasn’t really an option to begin with); we took a giant leap from where we were to the next thing.
But the thing is: we’ve been sitting there, in mostly that same spot, since we took that leap all those years ago.
My sense is: we’re beginning to near another leap.
I don’t know what it will be, or why. But I think we’re starting to feel all the problems React actually doesn’t solve for us, like we felt with jQuery back in those days. And I think eventually, it will be clear that it’s time to advance.
What will that new thing be? I don’t know. Maybe it’ll just be the web platform. Maybe we won’t even need frameworks. Maybe it’ll be a framework above; maybe it’ll be something we haven’t even seen yet. Maybe it won’t even be a thing; maybe there will be more diversity of tooling and less coalescing around one single accepted standard (though of all the above options, I’d say that seems the least likely, because again: humans. We’re busy little monkeys and so we like defaults.)
I think, though, that the delta between React and that thing, whatever it is, will continue to grow larger and larger over time.
So every new day is an even better day than the one before it to explore what you’ve been missing.