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>

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
 
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? :)

 
mattwelke profile image
Matt Welke

writing an Express API requires "old javascript" unless you want to go through the hassle of setting Babel up

Not sure what you mean here. You can use very recent JS features in Node. We have async/await, async iteration, ES module imports, and more now. I've never felt the need to set up Babel in an Express project.

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.

Collapse
 
caelumf profile image
CaelumF • Edited

I don't think it's necessarily true that only document navigation can provide a fast and install-less experience. I think from the beginning, distributing binaries and running them in browsers was not believed to be secure and there was rightfully no trust that different platforms would compile/interpret these complicated instructions the same way, so distributing documents was an easier path and it gained momentum.

Now however, you can expect browsers to support WASM, which is faster, lighter, has more secure packages, and really I think the reason that any applications are distributed as documents with scripts attached is because of the massive momentum differences. I think we will slowly see the abandonment of the web as we know it as more applications are distributed as binaries (not necessarily complete ones send at once, they can be requested too and render requested content text + images too) and the boundary of browser and OS blurs. I can't wait :)

Collapse
 
doublebackslash profile image
doublebackslash • Edited

I think the problem here is execution. I think there's the ideal of a fast, feature-full modern web (still maybe built on a foundation that isn't really made for it), but I think the issue is that it remains only an ideal.

The majority of attempts at executing on this ideal, I think fail to impress. They tend to be bloated, overly complex, and annoying to use.

In other words, the paradigm isn't necessarily the problem (at least not the whole problem), the people that build with it (or the clients that won't budge or have unreasonable requests) are the problem. I'm sure there are thousands of situations where developers have all the right priorities in mind, but simply aren't given the breathing room to implement them.

Shifting to a PWA paradigm means more can be accomplished, I suppose- but that includes the bad, and it seems par for the course to either:

  • Build something poorly or with a blind eye towards performance and users that don't have the awesome connection, PC, etc that the person building the app does
  • Use the PWA paradigm for products where it is absolutely unnecessary
Collapse
 
bjornhauge profile image
bjornhauge

A year and a half later, I think you're spot on.

Collapse
 
adrianus profile image
Adrianus • Edited

...or searching for something against vitamin D deficiency because it boosts the immune system, just an example of web (not app) traffic flow

  • result: enzymes that metabolise vitamin D need Magnesium
  • 'get Magnesium' Stripe, pick up spinach at the farmers market ($)
  • click here for Smoothie recipes,
  • 'you need a blender, you get a blender', upsell, done, ($)
  • sponsored by your local solarium ($)

According to Gartner most users, from 12% to 89% in six years, opt in for an experience ahead of price and product:
youtube.com/watch?v=vMmCxx-FiWo

It's all about moments guys, and some try to get rid of it, isolating experiences within apps, but:
think.storage.googleapis.com/image...

Collapse
 
rhymes profile image
rhymes • Edited

It strikes me that the web never was and never will be anything like any other platform for building applications. Whether or not someone wants to turn it into that is their prerogative and any success they have probably will help others build better things. But it ultimately depends on what you want to build and why, and emulating a native application might not always be the best option.

this :-)

Collapse
 
jon49 profile image
Jon Nyman

I think it is more use the right tool for the job. Some websites you can get away with mostly static with just a little bit of JS. Let's say you have a blog. Just some statically rendered HTML pages should be fine with some JS for the dynamic parts and then add in some instant.page functionality to preload a page. Maybe you could even add some transitions if it is preloaded, I have no idea.

Then for a business site maybe something like intercoolerjs could work.

Then when you want something that is fairly static but has some dynamic parts then you could use something like Svelte or some other lightweight framework.

I'm more of a back end dev. But I like to follow the front end world and experiment on the side. I'm building an offline first web site and using Stage0 for the more dynamic side of things. I'm using mostly vanilla js and small libs for everything else and it really hasn't been that bad of an experience.

The one thing that has been annoying with the way I'm architecting my site is that my templates are separate from my data. One of my goals was to keep the build steps minimal which has led me this way. But with something like Sinuousjs/Svelte/Sapper I could keep a small run time and get a better developer experience. So, for something that was more oriented for a team development environment I would probably lean towards those. But for my personal project it is fun learning all the native APIs and learning how to keep things lean on my own.

Collapse
 
jon49 profile image
Jon Nyman

And with the ability to use sidecar proxy (service workers) on the front end it makes writing a custom router really simple.