DEV Community

Rich Harris
Rich Harris

Posted on

In defense of the modern web

I expect I'll annoy everyone with this post: the anti-JavaScript crusaders, justly aghast at how much of the stuff we slather onto modern websites; the people arguing the web is a broken platform for interactive applications anyway and we should start over; React users; the old guard with their artisanal JS and hand authored HTML; and Tom MacWright, someone I've admired from afar since I first became aware of his work on Mapbox many years ago. But I guess that's the price of having opinions.

Tom recently posted Second-guessing the modern web, and it took the front end world by storm. You should read it, or at the very least the CliffsNotes. There's a lot of stuff I agree with to varying degrees:

There is a sweet spot of React: in moderately interactive interfaces ... But there’s a lot on either side of that sweet spot.

It's absolutely the case that running React in the client for a largely static site is overkill. It's also true that you have to avoid React if your app is very heavily interactive — it's widely understood that if you want 60fps animation, you will likely have to bypass the React update cycle and do things in a more imperative fashion (indeed, this is what libraries like react-spring do). But while all this is true of React, it's much less true of component frameworks in general.

User sessions are surprisingly long: someone might have your website open in a tab for weeks at a time. I’ve seen it happen. So if they open the ‘about page’, keep the tab open for a week, and then request the ‘home page’, then the home page that they request is dictated by the index bundle that they downloaded last week. This is a deeply weird and under-discussed situation.

It's an excellent point that isn't really being addressed, though (as Tom acknowledges) it's really just exacerbating a problem that was always there. I think there are solutions to it — we can iterate on the 'index bundle' approach, we could include the site version in a cookie and use that to show actionable feedback if there's a mismatch — but we do need to spend time on it.

It’s your startup’s homepage, and it has a “Sign up” button, but until the JavaScript loads, that button doesn’t do anything. So you need to compensate.

This is indeed very annoying, though it's easy enough to do this sort of thing — we just need to care enough:

<button class="sign-up" disabled={!is_browser}>
  {is_browser ? 'Sign up' : 'Loading'}
</button>
Enter fullscreen mode Exit fullscreen mode

But I'm not sure what this has to do with React-style frameworks — this issue exists whatever form your front end takes, unless you make it work without JS (which you should!).

Your formerly-lightweight application server is now doing quite a bit of labor, running React & making API requests in order to do this pre-rendering.

Again, this is true but more React-specific than anything. React's approach to server-side rendering — constructing a component tree, then serialising it — involves overhead that isn't shared by frameworks that, for example, compile your components (hi!) to functions that just concatenate strings for SSR, which is faster by a dramatic amount. And those API requests were going to have to get made anyway, so it makes sense to do them as early as possible, especially if your app server and API server are close to each other (or even the same thing).

The dream of APIs is that you have generic, flexible endpoints upon which you can build any web application. That idea breaks down pretty fast.

Amen. Just go and read the whole 'APIs' section several times.


Minor quibbles aside, Tom identifies some real problems with the state of the art in web development. But I think the article reaches a dangerous conclusion.

Let's start by dissecting this statement:

I can, for example, guarantee that this blog is faster than any Gatsby blog (and much love to the Gatsby team) because there is nothing that a React static site can do that will make it faster than a non-React static site.

With all due respect to those involved, I don't think Gatsby is a particularly relevant benchmark. The gatsby new my-site starter app executes 266kB of minified JavaScript for a completely static page in production mode; for gatsbyjs.org it's 808kB. Honestly, these are not impressive numbers.

The Lighthouse performance score for gatsbyjs.org

The Lighthouse score for Gatsby's homepage, obtained via webpagetest.org/easy.

Leaving that aside, I disagree with the premise. When I tap on a link on Tom's JS-free website, the browser first waits to confirm that it was a tap and not a brush/swipe, then makes a request, and then we have to wait for the response. With a framework-authored site with client-side routing, we can start to do more interesting things. We can make informed guesses based on analytics about which things the user is likely to interact with and preload the logic and data for them. We can kick off requests as soon as the user first touches (or hovers) the link instead of waiting for confirmation of a tap — worst case scenario, we've loaded some stuff that will be useful later if they do tap on it. We can provide better visual feedback that loading is taking place and a transition is about to occur. And we don't need to load the entire contents of the page — often, we can make do with a small bit of JSON because we already have the JavaScript for the page. This stuff gets fiendishly difficult to do by hand.

Beyond that, vanilla static sites are not an ambitious enough goal. Take transitions for example. Web developers are currently trapped in a mindset of discrete pages with jarring transitions — click a link, see the entire page get replaced whether through client-side routing or a page reload — while native app developers are thinking on another level:

It will take more than technological advancement to get the web there; it will take a cultural shift as well. But we certainly can't get there if we abandon our current trajectory. Which is exactly what Tom seems to be suggesting.


I'm not aware of any other platform where you're expected to write the logic for your initial render using a different set of technologies than the logic for subsequent interactions. The very idea sounds daft. But on the web, with its unique history, that was the norm for many years — we'd generate some HTML with PHP or Rails or whatever, and then 'sprinkle some jQuery' on it.

With the advent of Node, that changed. The fact that we can do server-side rendering and communicate with databases and what-have-you using a language native to the web is a wonderful development.

There are problems with this model. Tom identifies some of them. Another major issue he doesn't discuss is that the server-rendered SPA model typically 'hydrates' the entire initial page in a way that requires you to duplicate a ton of data — once in the HTML, once in the JSON blob that's passed to the client version of the app to produce the exact same result — and can block the main thread during the period the user is starting to interact with the app.

But we can fix those problems. Next is doing amazing innovation around (for example) mixing static and dynamic pages within a single app, so you get the benefits of the purely static model without ending up finding yourself constrained by it. Marko does intelligent component-level hydration, something I expect other frameworks to adopt. Sapper, the companion framework to Svelte, has a stated goal of eventually not sending any JS other than the (tiny) router itself for pages that don't require it.

The future I want — the future I see — is one with tooling that's accessible to the greatest number of people (including designers), that can intelligently move work between server and client as appropriate, that lets us build experiences that compete with native on UX (yes, even for blogs!), and where upgrading part of a site to 'interactive' or from 'static' to 'dynamic' doesn't involve communication across disparate teams using different technologies. We can only get there by committing to the paradigm Tom critiques — the JavaScript-ish component framework server-rendered SPA. (Better names welcomed.)

The modern web has flaws, and we should talk about them. But let's not give up on it.

Latest comments (87)

Collapse
 
jacobmakestheweb profile image
Jacob Ybarra

nah we should give up

Collapse
 
panesofglass profile image
panesofglass

I believe at the point you fundamentally push to render things other than markup, you start making a different application. And that is perfectly reasonable. I’m frankly surprised how well HTTP has held up to so many use cases. I long ago expected to see far more protocols for more kinds of apps, but HTTP seems to have solved so many problems well enough that it now hosts other protocols.

There are probably more protocols that could be written and hosted on other ports. Those would likely address many of these issues in a far better way. I just wonder when the industry will pivot back to building protocols and leave HTTP well enough alone.

Collapse
 
rhymes profile image
rhymes

I think we're past that as most recent innovations in the web space are either formats or evolutions of HTTP. Why reinvent the wheel if the wheel works well? What do you think?

Thread Thread
 
panesofglass profile image
panesofglass

HTTP/3 with QUIC seems like a great improvement, but it is changing HTTP. I don’t see why HTTP needs changing for most things. Why not update SMTP or FTP, as well? QUIC solves specific problems, and it might be better for those who need QUIC for it to have features better suited to their needs.

In some ways, this also feels like the mash of several formats into HTML5 to avoid name spacing. While we focus on and champion components within our own apps, we continue to avoid and break components in the infrastructure.

Therefore, I think we may see new protocols emerge as the weight of these complexities begin to cause problems. That could still be some way off.

Thread Thread
 
rhymes profile image
rhymes

I'm not sure it is changing HTTP, it's HTTP over UDP. The protocol is basically the same as HTTP/1 and HTTP/2.

QUIC solves specific problems, and it might be better for those who need QUIC for it to have features better suited to their needs.

QUIC solves a specific problem HTTP has because it sits on top of TCP though, basically pipelining

In some ways, this also feels like the mash of several formats into HTML5 to avoid name spacing. While we focus on and champion components within our own apps, we continue to avoid and break components in the infrastructure.

I'm not sure I follow the analogy with HTML5, sorry :( IIRC HTML5 was created to stop having to revise HTML as a a single unit and let things evolve at their own pace. Same with CSS 3 I guess. I don't think it's the same thing here: HTTP/3 is the result of the entire world using HTTP and needing to improve performance.

Could they have created a new protocol? Sure, but why break compatibility with millions of browsers, proxies, machines, software application and so on that understand and function through HTTP? I think the decision to rewrite the transport layer to be a smart one.

This doesn't mean that other protocols can't emerge, but it's okay not to throw away those that work already

Collapse
 
raresportan profile image
Rares Portan

Thank you for writing this. I wish more community leaders like you would take the time to talk about where we are what is the way forward.

Thank you for standing out for your beliefs and engaging in controversy with others that are all hyped up every time a tech claims The One True Way.

Collapse
 
zilti_500 profile image
Daniel Ziltener

Fulcro can also do server-side rendering before sending things to the client.
But, honestly... not everything should be in a browser. It's criminal and dangerous to do so. Because it takes away the data from the users and locks them into your platform. I know this is something businesses want to do, but it is bad for users.
Programs where actual work is created should be native software. Connecting information, and collecting it - sure, that has a place on the web. But stuffing entire office suites into a browser, or 3D programs, no, that is going to haunt us down the line.
Look what fat behemoths browsers have become these days. They reimplement half an OS because a few people decided that people are too dumb to download a program. Programs don't even have to be installed anymore these days, look at things like AppImage. It is download and run. Why not work on such things more?

Collapse
 
calvintwr profile image
calvintwr

Funny I went through the comments and I generally don't see Vue being mentioned. It is in my opinion the simplest framework out there, given that especially it was not trying to fancy anyone with powerful features using coding patterns only computer science majors understand.

I don't think we should be using React as a reference because engineers at Facebook isn't really the best bunch of people you want to look to when it comes to simplifying things.

I have in my younger days wrote modules and pride myself for having understood and applied advanced computer science concepts. I deleted all those, and now only write modules as simply as I can.

And I highly agree with this article. And next I think the only way we can get there is to, at every step of the way remember to KISS.. Keep it simple, stupid.

Collapse
 
navdove profile image
Navdeep Singh

When is next blog coming on Svelte? :)

Collapse
 
dinther profile image
Paul van Dinther

Several points

  1. running server-side code is expensive compared to just serving files but I am sure Amazon and Google love to sell you the cpu cycles.

  2. Modern browsers auto update and are fairly compliant to HTML5. I don't write extra code to support the wonky Apple Safari or Opera. So I don't use poly fills. Basically, if your IE6 doesn't work that is not my problem.

  3. Don't resort to npm if you need to know if a variable is an array. In other words, learn to code or at least learn to search for answers to your specific code problem.

  4. Desktop computers and phones are very capable computers able to generate and render their own HTML. For dynamic interaction nothing is faster then a lean purpose build piece of code running on the client.

  5. Stop using bullshit fantasy languages such as coffee script or typescript. Loose typing is a feature of JavaScript and works really well provided you write your own code and don't rely on the thousands of files pulled from npm.

  6. The above will get rid of packages, most build script tasks and other black box bloat that breaks the moment some script kiddy is bored and deletes his one liner idiot script.

  7. now you're down from several hundred dependencies to just 10 files or less you will find using deferred loading of the JavaScript perfectly manageable.

Client side UI libraries should run client side, generate their own HTML and bring along their own default css without dependencies. They should not force you into using any npm shit, complex build processes or use of modules.

In the old days of win32 API coding the UI classes were provided by the OS. Meticulously crafted to provide consistent visual feedback, keyboard support, indication for shortcut keys and so on.

I am working on just such a UI library unit which is about 1000 bytes in size capable to produce dialogs, pop-up menus, combo selectors, drop down and slide in menus looking as modern as those from Google without the need of any additional resources.

I tried many other so called libraries but there is always something missing. No arrow key support or the pop-up won't avoid window boundaries or its absolute bloat like Googles material thingy contraption.

Collapse
 
promikecoder2020 profile image
ProMikeCoder2020

Bro have you ever used typescript??? I used to use JavaScript. Most of the time there were hard bugs to find because of typing coerence problems and such. It was hell. Then I changed to typescript and boom: fewer bugs and a lot better autocomplete. And the best of it is that typescript doesn't affect your runtime performance since you can add it as Dev dependencie since it compiles to javascript ahead of time

Collapse
 
markoskon profile image
Markos Konstantopoulos

The Lighthouse result for Gatsby is unfortunate and a bit unfair. Even Lighthouse points out at the bottom of the image that values may vary. I think the performance tab of Chrome shows a better picture of what happens during page load.

 
gotofritz profile image
fritz

We need to have a similar approach in client side frameworks.

No we don't... if the The One True Omniscient God Framework approach that Rails developers are constantly pining for was really the best one, then Rails would be ruling. It isn't; it peaked in 2012 or thereabouts, knocked off its perch by Node, among other technologies. It's a single point of failure and it cannot adapt quickly enough to the bewilderingly fast changing environment in which we operate.

Alternatively, you can pick one of the two frameworks that follow the model you advocate: Ember (actually inspired by Rails) or Angular (more of a C# flavour), both of which strive to be a nanny that remove as much as possible the need to (god forbid!) make your own decisions or (the horror!) having to learn new tools

If I could have the framework of my dreams ... It would allow me to focus on HTML with a sprinkle of JavaScript.

You may want to consider stimulusjs.org/, which comes from the Rails universe (it was created by no other than David Heinemeier Hansson, the Rails Superstar) and does exactly what you want

Thread Thread
 
loilo profile image
Florian Reuschel

Can confirm that the Stimulus framework works great for that exact use case. But if you're used to the more common frameworks, you might quickly miss the declarative "give me data, I give you DOM" approach they provide.

I haven't tried it myself yet, but Alpine.js gained some steam lately and might be a good middle ground between Stimulus and the "top dogs".

 
caelumf profile image
CaelumF

Well a sever theoretically could send different WASM depending on the request path and request more content as "links" are pressed, not sure what you can call a website but the benefits of a website can be reaped without actually making a document now. I can imagine soon, if it doesn't already exist, server side rendering of HTML can instead output WASM + WebGL and speed up clients further. The point being that these "native" apps don't necessarily need installing and can still have the benefits of web apps and native apps combined :)

Thread Thread
 
ojrask profile image
Otto Rask

How would you define the layouting and styling of these WebGL apps in a way that does not result in every one of the apps becoming artisanal one-offs?

Thread Thread
 
jacobmakestheweb profile image
Jacob Ybarra

@caelumf so your saying we should write bytecode to generate documents? lol no.