The End : Focal Curve
I can’t remember the last time a blog post resonated with me this much.
Craig’s criteria on his job search:
- One: fuck offices
- Two: fuck AI
- Three: fuck React
And his conclusion:
Fuck work
I can’t remember the last time a blog post resonated with me this much.
Craig’s criteria on his job search:
- One: fuck offices
- Two: fuck AI
- Three: fuck React
And his conclusion:
Fuck work
I’ve had a lot of people recently tell me AI is “inevitable.” That this is “the future” and “we all better get used to it.”
For the last decade, I’ve had a lot of people tell me the same thing about React.
And over that decade of React being “the future” and “inevitable,” I worked on many, many projects without it. I’ve built a thriving career.
AI feels like that in many ways. It also feels different in that non-technical people also won’t shut the fuck about it.
Frameworks like React are often perceived as accelerators, or even as the only sensible way to do web development. There’s this notion that a more “modern” stack (read: JS-heavy, where the JS ends up running on the user’s browser) allows you to be more agile, release more often with fewer bugs, make code more maintainable, and ultimately launch better sites. In short, the claim is that this approach will offer huge improvements to developer experience, and that these DevEx benefits will trickle down to the user.
But over the years, this narrative has proven to be unrealistic, at best. In reality, for any decently sized JS-heavy project, you should expect that what you build will be slower than advertised, it will keep getting slower over time while it sees ongoing work, and it will take more effort to develop and especially to maintain than what you were led to believe, with as many bugs as any other approach.
Where it comes to performance, the important thing to note is that a JS-heavy approach (and particularly one based on React & friends) will most likely not be a good starting point; in fact, it will probably prove to be a performance minefield that you will need to keep revisiting, risking a detonation with every new commit.
I don’t like magic.
I’m not talking about acts of prestidigitation and illusion. I mean the kind of magic that’s used to market technologies. It’s magic. It just works. Don’t think about it.
I’ve written about seamless and seamful design before. Seamlessness is often touted as the ultimate goal of UX—“don’t make me think!”—but it comes with a price. That price is the reduction of agency.
When it comes to front-end development, my distrust of magic tips over into being a complete control freak.
I don’t like using code that I haven’t written and understood myself. Sometimes its unavoidable. I use two JavaScript libraries on The Session. One for displaying interactive maps and another for generating sheet music. As dependencies go, they’re very good but I still don’t like the feeling of being dependant on anything I don’t fully understand.
I can’t stomach the idea of using npm to install client-side JavaScript (which then installs more JavaScript, which in turn is dependant on even more JavaScript). It gives me the heebie-jeebies. I’m kind of astonished that most front-end developers have normalised doing daily trust falls with their codebases.
While I’m mistrustful of libraries, I’m completely allergic to frameworks.
Often I don’t distinguish between libraries and frameworks but the distinction matters here. Libraries are bits of other people’s code that I call from my code. Frameworks are other people’s code that call bits of my code.
Think of React. In order to use it, you basically have to adopt its idioms, its approach, its syntax. It’s a deeper level of dependency than just dropping in a regular piece of JavaScript.
I’ve always avoided client-side React because of its direct harm to end users (over-engineered bloated sites that take way longer to load than they need to). But the truth is that I also really dislike the extra layer of abstraction it puts between me and the browser.
Now, whenever there’s any talk about abstractions someone inevitably points out that, when it comes to computers, there’s always some layer of abstraction. If you’re not writing in binary, you don’t get to complain about an extra layer of abstraction making you uncomfortable.
I get that. But I still draw a line. When it comes to front-end development, that line is for me to stay as close as I can to raw HTML, CSS, and JavaScript. After all, that’s what users are going to get in their browsers.
My control freakery is not typical. It’s also not a very commercial or pragmatic attitude.
Over the years, I’ve stopped doing front-end development for client projects at work. Partly that’s because I’m pretty slow; it makes more sense to give the work to a better, faster developer. But it’s also because of my aversion to React. Projects came in where usage of React was a foregone conclusion. I wouldn’t work on those projects.
I mention this to point out that you probably shouldn’t adopt my inflexible mistrustful attitude if you want a career in front-end development.
Fortunately for me, front-end development still exists outside of client work. I get to have fun with my own website and with The Session. Heck, they even let me build the occasional hand-crafted website for a Clearleft event. I get to do all that the long, hard stupid way.
Meanwhile in the real world, the abstractions are piling up. Developers can now use large language models to generate code. Sometimes the code is good. Sometimes its not. You should probably check it before using it. But some developers just YOLO it straight to production.
That gives me the heebie-jeebies, but then again, so did npm. Is it really all that different? With npm you dialled up other people’s code directly. With large language models, they first slurp up everyone’s code (like, the whole World Wide Web), run a computationally expensive process of tokenisation, and then give you the bit you need when you need it. In a way, large language model coding tools are like a turbo-charged npm with even more layers of abstraction.
It’s not for me but I absolutely understand why it can work in a pragmatic commercial environment. Like Alice said:
Knitting is the future of coding. Nobody knits because they want a quick or cheap jumper, they knit because they love the craft. This is the future of writing code by hand. You will do it because you find it satisfying but it will be neither the cheapest or quickest way to write software.
But as Dave points out:
And so now we have these “magic words” in our codebases. Spells, essentially. Spells that work sometimes. Spells that we cast with no practical way to measure their effectiveness. They are prayers as much as they are instructions.
I shudder!
But again, this too is nothing new. We’ve all seen those codebases that contain mysterious arcane parts that nobody dares touch. coughWebpackcough. The issue isn’t with the code itself, but with the understanding of the code. If the understanding of the code was in one developer’s head, and that person has since left, the code is dangerous and best left untouched.
This, as you can imagine, is a maintenance nightmare. That’s where I’ve seen the real cost of abstractions. Abstractions often really do speed up production, but you pay the price in maintenance later on. If you want to understand the codebase, you must first understand the abstractions used in the codebase. That’s a lot to document, and let’s face it, documentation is the first casuality of almost every project.
So perhaps my aversion to abstraction in general—and large language models in particular—is because I tend to work on long-term projects. This website and The Session have lifespans measured in decades. For these kinds of projects, maintenance is a top priority.
Large language model coding tools truly are magic.
I don’t like magic.
Well, this is horrifying.
This is depressing.
React is no longer just a library. It’s a full ecosystem that defines how frontend developers are allowed to think.
Browsers now ship View Transitions, Container Queries, and smarter scheduling primitives. The platform keeps evolving at a fair pace, but most teams won’t touch these capabilities until React officially wraps them in a hook or they show up in Next.js docs.
Innovation keeps happening right across the ecosystem, but for many it only becomes “real” once React validates the approach. Which is fine, assuming you enjoy waiting for permission to use the platform you’re already building on.
Zing!
The critique isn’t that React is bad, but that treating any single framework as infrastructure creates blind spots in how we think and build. When React becomes the lens through which we see the web, we stop noticing what the platform itself can already do, and we stop reaching for native solutions because we’re waiting for the framework-approved version to show up first.
If your team’s evolution depends on a single framework’s roadmap, you are not steering your product; you are waiting for permission to move.
This isn’t a rhetorical question. I genuinely want to know why developers choose to build websites using React.
There are many possible reasons. Alas, none of them relate directly to user experience, other than a trickle-down justification: happy productive developers will make better websites. Citation needed.
It’s also worth mentioning that some people don’t choose to use React, but its use is mandated by their workplace (like some other more recent technologies I could mention). By my definition, this makes React enterprise software in this situation. My definition of enterprise software is any software that you use but that you yourself didn’t choose.
By far the most common reason for choosing React today is inertia. If it’s what you’re comfortable with, you’d need a really compelling reason not to use it. That’s generally the reason behind usage mandates too. If we “standardise” on React, then it’ll make hiring more straightforward (though the reality isn’t quite so simple, as the React ecosystem has mutated and bifurcated over time).
And you know what? Inertia is a perfectly valid reason to choose a technology. If time is of the essence, and you know it’s going to take you time to learn a new technology, it makes sense to stick with what you know, even if it’s out of date. This isn’t just true of React, it’s true of any tech stack.
This would all be absolutely fine if React weren’t a framework that gets executed in browsers. Any client-side framework is a tax on the end user. They have to download, parse, and execute the framework in order for you to benefit.
But maybe React doesn’t need to run in the browser at all. That’s the promise of server-side rendering.
There used to be a fairly clear distinction between front-end development and back-end development. The front end consisted of HTML, CSS, and client-side JavaScript. The back end was anything you wanted as long as it could spit out those bits of the front end: PHP, Ruby, Python, or even just a plain web server with static files.
Then it became possible to write JavaScript on the back end. Great! Now you didn’t need to context-switch when you were scripting for the client or the server. But this blessing also turned out to be a bit of a curse.
When you’re writing code for the back end, some things matter more than others. File size, for example, isn’t really a concern. Your code can get really long and it probably won’t slow down the execution. And if it does, you can always buy your way out of the problem by getting a more powerful server.
On the front end, your code should have different priorities. File size matters, especially with JavaScript. The code won’t be executed on your server. It’s executed on all sorts of devices on all sorts of networks running all sorts of browsers. If things get slow, you can’t buy your way out of the problem because you can’t buy every single one of your users a new device and a new network plan.
Now that JavaScript can run on the server as well as the client, it’s tempting to just treat the code the same. It’s the same language after all. But the context really matters. Some JavaScript that’s perfectly fine to run on the server can be a resource hog on the client.
And this is where it gets interesting with React. Because most of the things people like about React still apply on the back end.
When React first appeared, it was touted as front-end tool. State management and a near-magical virtual DOM were the main selling points.
Over time, that’s changed. The claimed speed benefits of the virtual DOM turned out to be just plain false. That just left state management.
But by that time, the selling points had changed. The component-based architecture turned out to be really popular. Developers liked JSX. A lot. Once you got used to it, it was a neat way to encapsulate little bits of functionality into building blocks that can be combined in all sorts of ways.
For the longest time, I didn’t realise this had happened. I was still thinking of React as being a framework like jQuery. But React is a framework like Rails or Django. As a developer, it’s where you do all your work. Heck, it’s pretty much your identity.
But whereas Rails or Django run on the back end, React runs on the front end …except when it doesn’t.
JavaScript can run on the server, which means React can run on the server. It’s entirely possible to have your React cake and eat it. You can write all of your code in React without serving up a single line of React to your users.
That’s true in theory. The devil is in the tooling.
Next.js allows you to write in React and do server-side rendering. But it really, really wants to output React to the client as well.
By default, you get the dreaded hydration pattern—do all the computing on the server in JavaScript (yay!), serve up HTML straight away (yay! yay!) …and then serve up all the same JavaScript that’s on the server anyway (ya—wait, what?).
It’s possible to get Next.js to skip that last step, but it’s not easy. You’ll be battling it every step of the way.
Astro takes a very different approach. It will do everything it can to keep the client-side JavaScript to a minimum. Developers get to keep their beloved JSX authoring environment without penalising users.
Alas, the collective inertia of the “modern” development community is bound up in the React/Next/Vercel ecosystem. That’s a shame, because Astro shows us that it doesn’t have to be this way.
Switching away from using React on the front end doesn’t mean you have to switch away from using React on the back end.
The titular question I asked is too broad and naïve. There are plenty of reasons to use React, just as there are plenty of reasons to use Wordpress, Eleventy, or any other technology that works on the back end. If it’s what you like or what you’re comfortable with, that’s reason enough.
All I really care about is the front end. I’m not going to pass judgment on anyone’s choice of server-side framework, as long as it doesn’t impact what you can do in the client. Like Harry says:
…if you’re going to use one, I shouldn’t be able to smell it.
Here’s the question I should be asking:
Why use React in the browser?
Because if the reason you’re using React is cultural—the whole team works in JSX, it makes hiring easier—then there’s probably no need to make your users download React.
If you’re making a single-page app, then …well, the first thing you should do is ask yourself if it really needs to be a single-page app. They should be the exception, not the default. But if you’re determined to make a single-page app, then I can see why state management becomes very important.
In that situation, try shipping Preact instead of React. As a developer, you’ll almost certainly notice no difference, but your users will appreciate the refreshing lack of bloat.
Mostly though, I’d encourage you to investigate what you can do with vanilla JavaScript in the browser. I totally get why you’d want to hold on to React as an authoring environment, but don’t let your framework limit what you can do on the front end. If you use React on the client, you’re not doing your users any favours.
You can continue to write in React. You can continue to use JSX. You can continue to hire React developers. But keep it on your machine. For your users, make the most of what web browsers can do.
Once you keep React on the server, then a whole world of possibilities opens up on the client. Web browsers have become incredibly powerful in what they offer you. Don’t let React-on-the-client hold you back.
And if you want to know more about what web browsers are capable of today, come to Web Day Out in Brighton on Thursday, 12th March 2026.
React exists as a profound perversion of the web platform. React has failed upwards to widespread adoption because it provides a “developer experience” that bypasses the hard parts. Like learning HTML, or CSS, or JavaScript. Even learning React itself is discouraged; that’s for adults, you should use meta-frameworks. React devs are burdened with multi-megabyte monstrosities before they’ve written a single line of code. You cannot fix “too much JavaScript” with more JavaScript and yet React devs are trained to
npm installuntil their problems become their users’ problems.
Framework monoculture is a psychology problem as much as a tech problem. When one approach becomes “how things are done,” we unconsciously defend it even when standards would give us a healthier, more interoperable ecosystem. Psychologists call this reflex System Justification.
The explains a lot about React-driven front-end development!
When a single toolset becomes the default, we don’t just prefer it, we build narratives that justify it. And that’s when a tool quietly becomes a gate or even a destructive force.
Tim recently gave a talk at Smashing Conference in New York called One Step Ahead. Based on the slides, it looks like it was an excellent talk.
Towards the end, there’s a slide that could be the tagline for Web Day Out:
Betting on the browser is our best chance at long-term success.
Most of the talk focuses on two technologies that you can add to any website with just a couple of lines of code: view transitions and speculation rules.
I’m using both of them on The Session and I can testify to their superpowers—super-snappy navigations with smooth animations.
Honestly, that takes care of 95% of the reasons for building a single-page app (the other 5% would be around managing state, which most sites—e-commerce, publishing, whatever—don’t need to bother with). Instead build a good ol’-fashioned website with pages of HTML linked together, then apply view transitions and speculation rules.
I mean, why wouldn’t you do that?
That’s not a rhetorical question. I’m genuinely interested in the reasons why people would reject a simple declarative solution in favour of the complexity of doing everything with a big JavaScript framework.
One reason might be browser support. After all, both view transitions and speculation rules are designed to be used as progressive enhancements, regardless of how many browsers happen to support them right now. If you want to attempt to have complete control, I understand why you might reach for the single-page app model, even if it means bloating the initial payload.
But think about that mindset for a second. Rather than reward the browsers that support modern features, you would instead be punishing them. You’d be treating every browser the same. Instead of taking advantage of the amazing features that some browsers have, you’d rather act as though they’re no different to legacy browsers.
I kind of understand the thinking behind that. You assume a level playing field by treating every browser as though they’re Internet Explorer. But what a waste! You ship tons of uneccesary code to perfectly capable browsers.
That could be the tagline for React.
React is no longer winning by technical merit. Today it is winning by default. That default is now slowing innovation across the frontend ecosystem.
Semantic HTML? Optional. Server-side rendering? Rebuilt from scratch. Accessibility? Maybe, if there’s time. Performance? Who cares, when you can save costs by putting loading burdens onto the user’s device, instead of your server?
So gradually, the web became something you had to compile before you could publish. Not because users needed it. But because developers wanted it to feel modern.
Everything’s optimised for developers – and hostile to everyone else.
This isn’t accidental. It’s cultural. We’ve created an industry where complexity is celebrated. Where cleverness is rewarded. Where engineering sophistication is valued more than clarity, usability, or commercial effectiveness.
“We’ve stripped React out of our highest-traffic user flows and replaced it with vanilla JavaScript using small, focused libraries for specific needs,” said the CTO of a streaming service. “Our page load times dropped by 60% and our conversion rates improved by 14%.”
I can’t recommend React to any project or customer anymore.
Using almost any other modern alternative, you will save time, money and nerves, even if you haven’t used them before.
Don’t stick to technology just because you know it.
Want to use all those great features that have been in landing in browsers over the past year or two? View transitions! Scroll-driven animations! So much more!
Well, your coding co-pilot is not going to going to be of any help.
Large language models, especially those on the scale of many of the most accessible, popular hosted options, take humongous datasets and long periods to train. By the time everything has been scraped and a dataset has been built, the set is on some level already obsolete. Then, before a model can reach the hands of consumers, time must be taken to train and evaluate it, and then even more to finally deploy it.
Once it has finally released, it usually remains stagnant in terms of having its knowledge updated. This creates an AI knowledge gap. A period between the present and AI’s training cutoff. This gap creates a time between when a new technology emerges and when AI systems can effectively support user needs regarding its adoption, meaning that models will not be able to service users requesting assistance with new technologies, thus disincentivising their use.
So we get this instead:
I’ve anecdotally noticed that many AI tools have a ‘preference’ for React and Tailwind when asked to tackle a web-based task, or even to create any app involving an interface at all.
I’m subscribing to this RSS feed.
Put the kettle on; it’s another epic data-driven screed from Alex. The footnotes on this would be a regular post on any other blog (and yes, even the footnotes have footnotes).
This is a spot-on description of the difference between back-end development and front-end development:
Code that runs on the server can be fully costed. Performance and availability of server-side systems are under the control of the provisioning organisation, and latency can be actively managed by developers and DevOps engineers.
Code that runs on the client, by contrast, is running on The Devil’s Computer. Nothing about the experienced latency, client resources, or even available APIs are under the developer’s control.
Client-side web development is perhaps best conceived of as influence-oriented programming. Once code has left the datacenter, all a web developer can do is send thoughts and prayers.
As a result, an unreasonably effective strategy is to send less code. In practice, this means favouring HTML and CSS over JavaScript, as they degrade gracefully and feature higher compression ratios. Declarative forms generate more functional UI per byte sent. These improvements in resilience and reductions in costs are beneficial in compounding ways over a site’s lifetime.
React is a non-transferable skill.
React proponents might claim that React will teach you modern UI, but from what I’ve seen it barely copes with modern UI.
autofocus is broken, custom elements don’t work in all but the experimental version, using any “modern” features likedialogor popovers requiresuseEffect, and the synthetic event system teaches you so little about how DOM actually works. This isn’t modern UI, it’s UI from 2013 at its inception. I don’t have the time left in my career to pick up UI paradigms that haven’t evolved much beyond from when Barack Obama was in office.When I mentor early career developers and they ask me what they should learn, I can’t say React, they don’t have time. I mean sure, pick up enough React to land you the inevitable job doing it, but it’s not going to level up your career.
“And so what we did is we started looking at, internally, all of the places where we’re using web technology — so all of our internal web UIs — and realized that they were just really unacceptably slow.”
Why were they slow? The answer: React.
“We realized that our performance, especially on low-end machines, was really terrible — and that was because we had adopted this React framework, and we had used React in probably one of the worst ways possible.”