This is pretty inspiring. I'm pretty interested to see their approach on the backend rendering. I've had some good experience in the good ol' days with server rendering.
I haven't used it personally BUT it seems to cover most things I'd use these days, including "new" topics like grid, filters, etc. CSS is relatively simple so something of a reference with some examples would be great.
An approach I use is to try and build something in css first, if I can't find a way to make it work then I'll break out the JavaScript.
Css is surprisingly good in places, but it also drives me up the wall as soon as I let myself expect to be able to do more things in it. Yesterday it was gradient masks.
If you are already familiar with basic CSS skipping to the middle is fine. A lot of modern CSS is just discovery of what you can do since none of it is really all that intuitive, but I generally end up googling "how can I do X effect in CSS" and seeing what the consensus is.
Just as some examples, you can do responsive bars that move all over the page, change their contents, have togglable collapse menus, have dropdown menus in those menus, etc all in plain CSS, but you can't have multiple columns of arbitrary sized elements in left to right top to bottom order.
Learning CSS is really just learning the contemporary limits of what you can do and then ruining your lovely responsive grids and flexboxes with absolute positioned px garbage when you have to support IE as penance for your crimes.
This is totally true. We've recently started to discover this paradox ourselves: when styling and layout is all done via CSS, and JS + other assets are cached, HTML is just as light weight as JSON over the wire. As ashamed as I am to admit it, our newest innovation appears to be "multi-page apps"!
I remember there was some ridiculous article here where the author claimed to build world's fastest web app with PReact. It was a hierarchical list of HTML5 features with some tick-boxes. Out of curiosity I converted the giant JSON blob to <ul><li> list. HTML was about 15% smaller than JSON. You could also do most of the "logic" of the app via styling and normal HTML controls.
The benefit of JS comes in incremental updates. When you update that list with a new item, you can use optimistic update and small payloads in JS, while in HTML you have to load the whole page again, which is slower.
Firstly, the application I was talking about[1] didn't have real updates.
Secondly, no one stops you from doing incremental updates with HTML except they way you design your data structures and UI.
In fact, HTML has something that JSON doesn't. Semantics. This means you can create a generic setup for incremental updates based, say, on element IDS.
The main issue I have with this idea is that in practice I've almost never found the difference in payload to matter all that much, even on bad connections.
In some cases, even sending the entire HTML document and diffing on that using morphdom (because redrawing does bring noticeable cost rather quickly) was a viable strategy even compared to the most optimal json payload.
I'd very much like to see some examples that prove me wrong though, because while I've done some experiments, I might be underestimating how complex many web apps are. It's mostly that the apps I built in hindsight were often better off with a more old fashioned server-side, morphdom, turbolinks (or maybe intercooler?) type approach.
It's not the payload size that matters really, but the latency of requests which you have to wait between interactions, making the UX synchronous and slow.
Good examples are, well, almost any modern and popular app. Like Trello for example. Users spend lots of time on the same page making micro-interactions, creating cards, moving cards around, adding tags, etc. You want to make the in-app experience as flawless as possible in these types of apps, and the initial JS size doesn't matter so much especially when it's cached. There's also lots of shared state and you might be saving multiple things on the server at the same time or uploading multiple files in the background while editing other parts. You might want to store data offline and sync later to the server. Once you have decent complexity, JS app with framework such as React help a lot.
> It's not the payload size that matters really, but the latency of requests which you have to wait between interactions, making the UX synchronous and slow.
I'd use websockets if that was an issue. That said, Trello is a good example where I do agree a web app is a great solution.
>We've recently started to discover this paradox ourselves: when styling and layout is all done via CSS, and JS + other assets are cached, HTML is just as light weight as JSON over the wire.
In other words, when the web is architected as intended, it works fine, even with javascript.
Imagine that. It's like the problem isn't javascript per se, but unnecessary complexity.
These are handy tricks, even in JS at scale, and I'd recommend their usage if you can (big if, business requirements come first for most of us). That said, I don't really buy into the no JS/purism movement for load/execution speed. For bite-sized apps or simple pages, sure. However, it's completely possible and not that hard to develop full-blown web application suites using full JS for everything (structure, style, and function) and not have issues with load or speed. I'm not sure if there's a footgun here, but the issue falls squarely on the developers.
Seems like once again people just need to pick the right tool for the job. The problem is that people get stuck into one framework and it’s familiar to them so it gets forced into a project it doesn’t make sense for down the road.
Couldn't agree more. Anybody sitting down to start a _serious_ project should spend some time possibly researching and definitely planning, which would hopefully lead to the right decision here.
"people just need to pick the right tool for the job" is utterly useless advice, just as "you should do the right thing" is an utterly useless answer to an ethics question, because it doesn't help you decide what the "right" tool/thing is.
There are lots of considerations in answering that usefully. Fit for the particular problem domain is one of them, yes. But so is familiarity. And ease of hiring. And performance, stability, maturity, state of tooling, community, training resources and a zillion others. How do you balance all those? Wait, let me guess, "correctly".
Sorry about the snark, this has just been a personal peeve of mine ever since the Slashdot days, when any word salad whatsoever would get upvoted Insightful provided it contained the magic phrase "right tool for the job".
It wasn't posited as advice to a specific question, but rather a guiding principle, which is still valid and bears repeating given the frequency of the mistake.
I don’t deny any of those things. I think you’re reading way too much into my statement. I understand why people pick familiar tools and the cost of hiring and training people and getting up to speed on new stuff. It’s always a balance, and it’s why I don’t really like these posts that completely dismiss JavaScript and act like it has zero benefits.
I was on a team recently that only wanted to do Rails apps. They only wanted to make monolithic apps. Any mention of a more modular design would be met with derision. I don’t think this attitude is unique, though. And it’s really hard to overcome and see balance when the entire team clearly has no interest in changing or updating their skill set.
The whole thing is a solved problem. You can easily render React apps on the server and send HTML down the wire, then lazy load the rest of the JS. Author could have installed next.js and called it a day.
If you put your app logic in something like Redux it'll work on server-side rendering, client-side rendering, and can be included in react-native down the line.
In the time the author re-wrote his app, I could have gotten the same benefits with SSR and ported it to react native. But yeah, javascript bad REEEE
> I could have gotten the same benefits with SSR and ported it to react native
Are you saying that rhetorically? In my experience, React is no silver bullet (e.g. different router and styling mechanisms for RN), trickiness dealing w/ timezones in SSR, etc.
And besides, the point here was largely about bundle size and fast time-to-interactive. Next.js optimizes a lot for developer experience, but you're definitely still going to be shipping a pretty sizeable JS bundle with slower characteristics than a hand-coded "closer-to-the-metal" thing.
> Author could have installed next.js and called it a day.
Not a day, but probably a week of debugging. Each and every suggestion you made in your comment makes the deliverable tremendously more complex, is very expensive, will need ongoing maintenance, and is ultimately not needed.
Javascript apps are tremendously complex, why would do this if you don't have to?
Imagine you have a team of rails developers and you're asked to create a simple portal. Rails as a tool may be "overkill" but what does it matter?
If you already know Rails I'd say use that. If you only knew ruby then reaching for rails would be overkill. But if your business requirements need the features of Rails, learn rails instead of rolling your own framework..
I completely agree with you, which is why I think that telling people to "just install next.js" is a terrible advice. If they are JS devs, experienced in next, sure, go right ahead, but I don't think that's the case here.
I'd be fine to just use Rails for something like that since I'm quite familiar with Rails, but I would never recommend it to someone who doesn't know it.
> If they are JS devs, experienced in next, sure, go right ahead, but I don't think that's the case here.
The author is clearly experienced in UI development. The app was already written in React/Redux. So honestly, yeah, just install next.js and fix your routes.
This solution would be a complete non starter for any professional project in terms of accessibility - there's a few issues screen readers would run into with this. Not only that, but good luck getting other developers to sift through your spaghetti code.
But the main thing is, the authors goals are somewhat pointless. If you don't want a SPA then just use jQuery. The author ended up using plugins anyways so the whole story was aimed at people who don't know front-end but latch onto this idea of javascript is baaad. Don't be that guy.
So to recap. If you don't know javascript, and you don't want to learn the standard tooling - please don't roll your own framework and blog about it.
A lot of people here (including the author and myself) who have written multiple production applications using React and Redux will know that "just install next.js and call it a day" is a wild oversimplification. In fact, some of us were earlier adopters and enthusiasts of SPAs and flux architecture. You're seeing more and more write-ups on the problems with this approach because the early adopters are now long past the honeymoon and are entering long term maintenance.
Since I'm caching and gzipping everything, each subsequent pageview is around 6 KB; far smaller than the SPAs I've seen with equivalent functionality.
That's ace. But my internet connection has a 2 second latency so I still have to wait an annoyingly long time every time I interact with your app. My connection is terrible too, so it drops every 10th request, and now I'm seeing a lot of broken pages. If only you'd written an offline-first PWA and used background-loading and prefetching with some intelligence to retry failed requests in order to deliver content before I actually need it this would have worked so much better.
But hey, the app is perfect for you on your robust wifi, so it must work out there in the real world, right?
Snark aside, there's a good reason to build web software in a variety of different ways. Pick the one that suits your users. Don't assume everything works perfectly and every internet connection is strong, fast wifi. That just means a lot of users (especially poorer ones in rural communities) are going to struggle with what you're selling.
Agree that an offline-first PWA is the way to go. Not _no_ javascript, just _as little as possible_. However, I've also not seen such a good example before of how doable this is, so thanks to the author for the show-and-tell. The label/checkbox example is a new one on me, and covers pretty near 80% of my use of javascript. It's interesting how when you strip html of all the DOM api nonsense it starts to look like an exciting new framework. Forms are such a nice, declarative way to get user input.
If the app saves data to a server, that's gonna happen regardless anyways (and one could argue that techniques like optimistic updates might make the experience even worse)
Also remember that HTML can be streamed and partially rendered even if it's not fully downloaded, and the investment required to enable this is minimal. The same cannot be said about a SPA's data fetching/rendering strategies.
Having worked on bloated apps before, I despise when people throw around the word "prefetching" as if it's some sort of magic spell. At best it eats my battery and bandwidth needlessly, and at worst, it's weasel wording for slowing down time-to-first-pixel :(
Did you try using the site with the inspector open with Poor 2G throttling? I was still able to interact with the page within milliseconds. Just had to wait for the font to download before text would appear. Then after that, all of the assets besides the HTML was cached.
I think that's pretty damn good; to solve the 2 second latency, I think the developer is better off using a CDN or Netlify's ADN so that your site is delivered from a server closer to the user.
I keep hearing offline-first trumpeted, but I have seen no examples of what that means.
Terrible connections exist. But I fail to see how adding more scripting will always help with that. Service workers are definitely promising, but documentation for common use cases is still lacking.
Even with a terrible connection, I would still trust a server-side app to load correctly on a refresh more than a SPA.
If anything, the minimal use of Javascript is as inclusive as a web app can be.
I think they're talking about mobile twitter, where every time I visit I see the message "something went wrong". Then I tap retry and get the same message. It's very offline first. Then I reload the whole page and get the online version.
> But I fail to see how adding more scripting will always help with that
Your app has no control over browser connection management. JavaScript you write is aware of idempotency and retry semantics of your app, so it can manage requests more intelligently than the browser, without disrupting the user experience.
So sure, it won't always help if you do it badly, I can see how it always helps if you do it well.
Henrik Joreteg (https://twitter.com/HenrikJoreteg) makes some pretty good arguments for PWAs. I've not had the chance to work on one, but when you start looking at mobile devices in particular, they do seem to be a good middle ground between the slimness of a document (web page) and the capability of a native mobile app. They're also not mutually exclusive with "no javascript" (if you do it right, which is a consideration). And they have the potential to save developers work, since you can get away with a PWA instead of native apps on 2+ platforms.
I'm very much on board with the promise of PWAs, but so far I've never found an approach that didn't feel too complicated/tedious. That said, I've not made serious attempts in at least a few months, maybe a year. Would you say the situation has solidified/standardized in the interim, and if so, could you point me to some resources?
Unfortunately, I'm in the same boat, having only fiddled briefly with some of the newer web APIs, rather than architecting an app around service workers. My impression comes from folks like Joreteg who do the work, and from the increasing frequency with which new native-like APIs are released into browsers (though almost invariably implemented badly).
That said, even though the way service workers are registered is sort of odd, they're pretty trivial to work with once you're there. As far as resources go, MDN is always pretty solid, and this post (https://developers.google.com/web/fundamentals/primers/servi...) on Google's web dev guides is what I used to familiarize myself with them.
The neat thing is, an app using Service Workers doesn't have to be an SPA. You can use a Service Worker to just cache HTML as delivered from your server. E.g. I've seen blogs having a small service worker that just cached the last 5-10 accessed articles and added a custom error page saying "sorry, you are not online right now, but you can read the following articles from cache:"
> If only you'd written an offline-first PWA and used background-loading and prefetching with some intelligence to retry failed requests in order to deliver content before I actually need it this would have worked so much better.
Then I would have waited too long to load the page the first time and give up.
The code you received the first time you load the app should be identical to the server-rendered version with a single additional script tag that loads a small JS file asynchronously. That script then progressively enhances the page to make it a PWA with features that take advantage what your browser supports.
Not enough developers build this way yet though because it's quite hard work. In my opinion the answer is to use what browsers are capable of; it should not be to ignore the browser and do as much as possible on the server.
Who has good heuristics for retry (or other techniques) to achieve reliable communications for a PWA?
We do retries, with a few different strategies, but we have a poor idea of exactly how effective (or not) our retries are. Also my code for this is ugly and it's a nasty area to test properly...
> But my internet connection has a 2 second latency so I still have to wait an annoyingly long time every time I interact with your app. My connection is terrible too, so it drops every 10th request, and now I'm seeing a lot of broken pages.
I often work on a train over a 4G connection, so this describes me too. I much prefer traditional style web applications over SPAs specifically because of this.
You know what happens if a request is taking too long or fails with a traditional web app? I hit reload and it retries.
You know what happens if the same thing happens with most SPAs? It throws away my unsaved data and puts me back on the starting screen.
Sometimes there's a JavaScript-powered retry I can use instead. Sometimes it works, usually it is very unreliable (if something has failed to load, often the JavaScript is having problems too).
Sure, I'm willing to accept the possibility that an SPA can be written in such a manner as to compensate for this hostile environment. It's just: I've used a lot of these apps, and I haven't found one that works reliably over a poor connection yet.
> You know what happens if the same thing happens with most SPAs? It throws away my unsaved data and puts me back on the starting screen.
This is just bad design though. Obviously the dust isn't settled on the whole html5 technology yet, but for anyone who is wondering how to make an SPA more resilient to network instability, the solutions are: axios-retry, idempoteny PATCH/PUT, service workers, basic error handling, and always have the URL represent where the user is.
>This is just bad design though. Obviously the dust isn't settled on the whole html5 technology yet, but for anyone who is wondering how to make an SPA more resilient to network instability, the solutions are: axios-retry, idempoteny PATCH/PUT, service workers, and basic error handling.
Thing is, you get proper error messages, the ability to retry, warnings if you're about to repeat an unsafe operation etc out of the box if you don't use a SPA and render things server-side. The browser does this for you.
Sure, you can do a better job. But I've lost count of the number of SPAs and JS-driven apps where connection error = infinite spinning loader icon and no feedback or ability to retry. If you're going to willingly throw away the routing you get from HTTP, the progress indicators you get from actual links and non-AJAX requests and the browser error handling you better make damn sure you implement it all again in JS and provide feature parity for the user.
It's not just HTTP either - accessibility features like keyboard navigation often aren't implemented in React components because folks only write an `onClick` handler. Sure - the devs should've loaded "eslint-plugin-jsx-a11y
" and enforced rules that prevent this sloppiness on their CI - but it's far too easy to do it wrong and so devs do do it wrong and the users suffer from a poor experience as a result.
I've seen pure server based applications neglect all of those things, including destroying your input on errors, so it's not really helpful for anyone who actually needs an SPA.
so many older job applying application tend to be built in this way. Its happened way too often that I have filled in a whole page of info, from my birth to every job I have ( even though I literally have a resume just for that ), the browser crashes and I lose everything.
It's also possible to cache server-rendered pages offline with a service worker. Admittedly not many people do this, but there's no reason why they couldn't, and it would probably be smaller for a lot of sites.
It’s great that you have an opinion about the benefits of a SPA for slow connections and other people (lower comments on this thread) have opinions about how “easy” it is to implement request retries and caching/service workers.
I have an opinion too. But my opinion and your opinion really are moot.
Because do you know whose opinion is probably backed by the most data and the most expertise? Google’s Gmail team.
And Google, with all its amazing developers, who are arguably at the forefront of javascript standards and general web app innovation, and who have literal billions of visits, they have made the decision to give you a link and tells you to use a basic, server-side rendered web app if you have a “slow connection.” And they do this while attempting to boot up all the PWA/SPA framing of the main app, just in case, I don’t know, all that stuff fails.
So I think I’m going to give Gmails opinion a lot of weight here
Gmail's slow connection message is great, but it's there because Gmail is a multi-megabyte app that will take a long time to download if you're on dial up or something. That's a bandwidth issue rather than latency or line quality problem. None of the points I made say anything about bandwidth.
I completely agree with the point you're making though; if a user is on a low bandwidth connection then sending as little data as possible is best, and that's likely to be plain HTML with very little rich content like images.
That said, Gmail's slow connection UI is horrible to actually use. Very few people choose it over normal Gmail. Most people want the big fancy app, so we as developers should work out the best way to deliver that.
> If only you'd written an offline-first PWA and used background-loading and prefetching with some intelligence to retry failed requests in order to deliver content before I actually need it this would have worked so much better.
This is a unicorn. If it's the real world we're talking about, as far as I can tell client-heavy webapps that are actually engineered this well -- and don't frequently end up in a more inscrutable state than a pageload failure when network issues rear up -- are rare enough in practice that it feels weird to hold that up as the yardstick for criticism.
And if the point is that more applications should get there, well, that's more or less orthogonal to the point of the article, which (in spite of the headline) isn't so much that no one should use JS for anything ever (since the author details the JS he made a thoughtful choice to include), but that a notable range of UI behaviors that are frequently implemented using JS without a second thought don't necessarily have to be, and there are plausible benefits to the alternative. So this isn't exclusive with background loading / prefetching.
Define better... Scripted languages can be run without the need for a compilation step, if you're scaling horizontally anyway, you can do nearly as well with node, and node can handle 100k+ connections per core with minimalish memory overhead. Functions are first class, functional approaches are easy and flexible, and when you're just passing bytes to another system or database, JS/Node works well.
As to interpreted languages, JS/V8 has probably seen more optimization than any other interpreted language. As to Java, you don't need a 400-500mb base image either. Rust takes much longer to develop against, as does erlang even if you're familiar with it. If you're parsing JSON into concrete objects, the overhead is often larger with compiled languages than interpreted, where JS just works. I can generally get stuff working in around 1/8 the time it takes for peers to use a compiled language to do the same.
Go is probably a better option than the three you suggested, even then more often than not, I would choose node, just because of the flexibility I get with npm and the larger community. If I, in turn need to eek more performance, or have a process that requires more compute per request, then I'll often reach for another language and/or even choose a Function as a Service wrapper.
Why not C++17 in combination with h2o? I don't know why the compilation step is an issue if compilation and linking takes less than a second which is less time than switching back to the browser. I can pass around lambdas with ease and my lines and functions have become much more succinct with the newer versions especially when dealing with web related stuff. In addition, the https://github.com/nlohmann/json lib make json just work.
No one seems to get that the C++ still works and in nearly all cases is faster, compiles very fast and is very quick to develop with now that everyone has shared their code and since functional algos have been added to the stl. This all happens with the memory safety people require since we have shared and unique pointers and Intel's thread building blocks. My fully featured application is 3.7 MB and takes 12-15 MB of ram to run and handles 100k+ requests per second from my laptop. Those stats aren't even that good with the exception of the rps.
If going fully native isn't your game then how about nginx with the plethora of 3rd party modules that are supplied by other developers? Maybe you just really like js? Maybe js isn't the best but you're going to use it anyway because you know it and other people have already written a lot of your code for you? Maybe you just feel like wasting money on more machines instead of one vps instance and a cache-everything cdn?
Who says I need multiple machines? I just said if you're going to design for horizontal scale, then it becomes less of an issue. Also, there's a lot to be said for most of the code available already written. My time is worth far more than paying a couple extra $ a month to run an app on a second server/vps.
I just said that JS isn't so bad, and that language snobbery is worse when it means you're far less productive.
We get that C++ still works, but we also get that unless you can have an iron grip on the team, third party libraries and CI/CD pipeline, someone will definitely write C in C++, or forget about all those 200+ UB cases, and then all is lost.
I know... used to do CGI with C++ apps and Perl in the nid 90's. I was being snarky, but there are a lot of tools to do web requests in lower languages, and classic CGI did work well enough in a lot of ways, though the security concerns now are huge.
Honestly, a lot of this comes down to ignorance on a lot of points. You can do a lot with CSS2.x+ selectors, and it's awesome. That said, JS/JSS and component systems aren't bad either. It depends on what you're creating and who your audience is.
Most of this stuff isn't new, it's about a decade+ old at this point. Backend developers often tend to turn up their noses on web front ends, so they don't bother to actually learn anything meaningful, and just sit complaining constantly.
Given that Edge has less than 5% marketshare globally (depending on the source of the information), I would say that fretting over compatibility with it is not a priority. And given that MS is moving Edge to Chromium rendering engine, I assume this will solve the compat problem.
5% can still be a decent number of users, and tends to trend even higher in certain segments (state and local government, smaller "old economy" businesses)
It's a basic DOM element — a block of content. Without native support, it's simply shown by default, instead of hidden by default, so nothing is broken, and even the default on non-supporting browsers can be changed with JS and CSS.
If anything, this is the works everywheriest of things that "work everywhere" in post-2010 web development. Some other stuff actually breaks accessibility by default.
This is pretty much how I develop most of my web stuff. I do use JS, but it comes in afterwards, in discrete packages. It process specific HTML attributes to make the page do extra stuff, or accelerate certain operations, or stich pages together via AJAX. All scripts are stateless, because all state is stored in HTML.
The key is to represent your data and UI state via sane HTML. This article demonstrates this well with the modal popup example. Once you represent popup's state with a checkbox, you can manipulate that checkbox in any way you want and you can style it in any way you want and your styling will not have any dependencies on the methods of manipulation.
Im not sure how elegant I think the CSS hack is compared to just making a simplistic script for it. Would a user really notice the parsing time for such a simple javascript application?
I object to considering the techniques in the article "hacks". They all employ minimal markup, without extra boxes or complicated classes, and the CSS side is very simple.
I give you that its not a verbose or complicated hack. but I still would say that a hidden checkbox acting as the middle man for modals etc is pretty hacky :). A hack doesn't need to be complicated, most times its not. But after all I can't imagine anyone thinking about this as a feature when they implemented checkbox inputs back in the days.
I agree that it's a hack and I can't say that I like it. Blame the W3C and browser vendors for providing incomplete/barely functional standards like <dialog>.
This write-up mostly highlights the loading speed and bandwidth benefits but not requiring javascript is far better than just that. In the age of spectre and with browsers exposing more and more bare metal functionality the idea of running arbitrary code on every random site I go to is absurd. There's dozens of us that simply don't run JS. Sites like these (and, say, the way chicago public media does fallback for no JS) show that there can still be pretty, functional sites without the modern equivalent of opening that .exe attached to a random email.
I saw some actual statistics recently on a link from a blog with a lot of posts about accessibility (I forget which). It said that 1 in 98 users (1% of visitors) didn’t load JavaScript in a page, and 0.2% of visitors didn’t load the script because they had explicitly disabled JavaScript, as opposed to other reasons such as a poor network connection.
The paradigm shift here is that most of these things would be done in the server using traditional techniques: ajax, events, state are all handled via form submissions. The only things in this list that are really "frontend-ish" are element reuse (which, for styling purposes, is accomplished via CSS methodologies) and toggling things on/off, which in the CSS-only corners of the web is accomplished using a radio box CSS trick.
Behavioral augmentation that these days is encapsulated via componentization (e.g. tabs, carousels) is also something that has been explored to death in traditional paradigms, e.g. unobtrusive js
We all knew these tricks and started from there back in the day. Don't you think they are way too hacky? and cause too many page refreshes? Have you ever maintained codes mixing server-side templating and JS scripts?
And there are so many edge cases popping up so that you have to use JS anyway. Then you may ask why don't I use JS for everything then?
JS is inevitable no matter how you and I hate it. "JS-Free" only makes sense for simple presentations.
Some things like the radio box trick are hacky, I agree, but many things are not. Personally, I like to strike a balance between old-school techniques and new technology (e.g. use CSS for hovers instead of instagram-style DOM manipulation via JS)
To me, this whole thing about JS-Free is a backlash against overdoing things in JS, and it's as good time as any to rethink today's status quo, especially in regards to how many babies we have thrown out w/ the bath water when adopting JS-first technologies.
Nice work, I definitely appreciate the focus on simplicity/speed. I just signed up to check it out.
I noticed on the invoice page, on mobile, the number inputs (qty, price, etc) showed the full alpha keyboard on input. I think you can add a few attributes to optimize, without using JS also.
Thank you! I had no idea that iOS required the pattern="\d" attribute. Those inputs require a decimal, so that pattern won't actually work (something like pattern="\d\.?\d*"). If I can get my hands on an iPhone to test that, I will deploy it shortly!
Definitely a few hacks involved. I’ve seen different combinations of using pattern, type=number, step=“0.01” etc, but you may need support for longer decimals in qty/price.
I’m always on the lookout for form best practices, but it’s definitely hard to stay up with all of the current trends.
There are a few mobile preview tools, not sure which is currently best, for testing on different virtual devices:
I think you missed the entire point of the article. Your account also has 2 comments, and both of them are "weird flex, but okay". You are in the wrong place if you expect those comments to be appreciated.
That seems a bit extreme. IMO that's a bit like saying "use zero Elixir/Erlang on the backend". I think a few people would take more contention with that line—and they're both languages running on VM's with a specific performance target.
Absolutely, and a ton of commenters (mostly on Reddit) seem to be missing that point. Use the right tool for the job. In the article I explicitly state that I couldn't have built the drag-and-drop invoice editor without JavaScript (although I wish that I could!).
A lot of things are performed on the server, and then the server responds with a "flash" which is basically a little bar that says "Data saved." or "There was an error: ...".
There are a few places where I used a simple 5-liner to validate a field or something.
Unfortunately there was one place where I needed some heavier JS (for which I used Mithril.js), but it was absolutely required for when you're editing an invoice. You wouldn't want to edit line items and wait for a server response every time you make one change, so I simply determined that this was an area where I had no choice. Mithril simply modifies the DOM inside the <form> element, and then saving is a plain old POST request when you submit the form after you've made all of your changes.
The unfortunate reality is that I couldn't completely free myself from JavaScript, but got a lot closer.
Its generally good practice to start with forms and only start doing fetch requests for things you can demonstrate hamper user experience requiring refreshes.
212 comments
[ 287 ms ] story [ 576 ms ] threadDoes anyone know of a good book on intermediate/advanced CSS to help avoid excess JS usage/complexity?
I haven't used it personally BUT it seems to cover most things I'd use these days, including "new" topics like grid, filters, etc. CSS is relatively simple so something of a reference with some examples would be great.
Css is surprisingly good in places, but it also drives me up the wall as soon as I let myself expect to be able to do more things in it. Yesterday it was gradient masks.
https://developer.mozilla.org/en-US/docs/Web/CSS
If you are already familiar with basic CSS skipping to the middle is fine. A lot of modern CSS is just discovery of what you can do since none of it is really all that intuitive, but I generally end up googling "how can I do X effect in CSS" and seeing what the consensus is.
Just as some examples, you can do responsive bars that move all over the page, change their contents, have togglable collapse menus, have dropdown menus in those menus, etc all in plain CSS, but you can't have multiple columns of arbitrary sized elements in left to right top to bottom order.
Learning CSS is really just learning the contemporary limits of what you can do and then ruining your lovely responsive grids and flexboxes with absolute positioned px garbage when you have to support IE as penance for your crimes.
https://www.packtpub.com/web-development/professional-css3
https://www.amazon.com/CSS-Secrets-Solutions-Everyday-Proble...
Secondly, no one stops you from doing incremental updates with HTML except they way you design your data structures and UI.
In fact, HTML has something that JSON doesn't. Semantics. This means you can create a generic setup for incremental updates based, say, on element IDS.
---
[1] https://hackernoon.com/10-things-i-learned-making-the-fastes...
In some cases, even sending the entire HTML document and diffing on that using morphdom (because redrawing does bring noticeable cost rather quickly) was a viable strategy even compared to the most optimal json payload.
I'd very much like to see some examples that prove me wrong though, because while I've done some experiments, I might be underestimating how complex many web apps are. It's mostly that the apps I built in hindsight were often better off with a more old fashioned server-side, morphdom, turbolinks (or maybe intercooler?) type approach.
Good examples are, well, almost any modern and popular app. Like Trello for example. Users spend lots of time on the same page making micro-interactions, creating cards, moving cards around, adding tags, etc. You want to make the in-app experience as flawless as possible in these types of apps, and the initial JS size doesn't matter so much especially when it's cached. There's also lots of shared state and you might be saving multiple things on the server at the same time or uploading multiple files in the background while editing other parts. You might want to store data offline and sync later to the server. Once you have decent complexity, JS app with framework such as React help a lot.
I'd use websockets if that was an issue. That said, Trello is a good example where I do agree a web app is a great solution.
In other words, when the web is architected as intended, it works fine, even with javascript.
Imagine that. It's like the problem isn't javascript per se, but unnecessary complexity.
https://www.youtube.com/watch?v=Yx6k6WR8GRs
There are lots of considerations in answering that usefully. Fit for the particular problem domain is one of them, yes. But so is familiarity. And ease of hiring. And performance, stability, maturity, state of tooling, community, training resources and a zillion others. How do you balance all those? Wait, let me guess, "correctly".
Sorry about the snark, this has just been a personal peeve of mine ever since the Slashdot days, when any word salad whatsoever would get upvoted Insightful provided it contained the magic phrase "right tool for the job".
I think it’s an appropriate response to blanket advice on choice of tech stacks.
I was on a team recently that only wanted to do Rails apps. They only wanted to make monolithic apps. Any mention of a more modular design would be met with derision. I don’t think this attitude is unique, though. And it’s really hard to overcome and see balance when the entire team clearly has no interest in changing or updating their skill set.
If you put your app logic in something like Redux it'll work on server-side rendering, client-side rendering, and can be included in react-native down the line.
In the time the author re-wrote his app, I could have gotten the same benefits with SSR and ported it to react native. But yeah, javascript bad REEEE
Are you saying that rhetorically? In my experience, React is no silver bullet (e.g. different router and styling mechanisms for RN), trickiness dealing w/ timezones in SSR, etc.
And besides, the point here was largely about bundle size and fast time-to-interactive. Next.js optimizes a lot for developer experience, but you're definitely still going to be shipping a pretty sizeable JS bundle with slower characteristics than a hand-coded "closer-to-the-metal" thing.
No.
> And besides, the point here was largely about bundle size and fast time-to-interactive.
As long as the point wasn't to be productive I'll just mind my own business.
Not a day, but probably a week of debugging. Each and every suggestion you made in your comment makes the deliverable tremendously more complex, is very expensive, will need ongoing maintenance, and is ultimately not needed.
Javascript apps are tremendously complex, why would do this if you don't have to?
If you already know Rails I'd say use that. If you only knew ruby then reaching for rails would be overkill. But if your business requirements need the features of Rails, learn rails instead of rolling your own framework..
I thought people use Rails mainly because they like it or are thrown into a team that already uses it.
I'd be fine to just use Rails for something like that since I'm quite familiar with Rails, but I would never recommend it to someone who doesn't know it.
The author is clearly experienced in UI development. The app was already written in React/Redux. So honestly, yeah, just install next.js and fix your routes.
This solution would be a complete non starter for any professional project in terms of accessibility - there's a few issues screen readers would run into with this. Not only that, but good luck getting other developers to sift through your spaghetti code.
But the main thing is, the authors goals are somewhat pointless. If you don't want a SPA then just use jQuery. The author ended up using plugins anyways so the whole story was aimed at people who don't know front-end but latch onto this idea of javascript is baaad. Don't be that guy.
So to recap. If you don't know javascript, and you don't want to learn the standard tooling - please don't roll your own framework and blog about it.
Edit: looks like someone already commented this on his blog. This is all you need to know: https://dev.to/isfotis/comment/9c7k
"I did actually use some JavaScript on the new Slimvoice, but only when an interaction could not be replicated in any other way."
Overall, the article does point in a good direction.
That's ace. But my internet connection has a 2 second latency so I still have to wait an annoyingly long time every time I interact with your app. My connection is terrible too, so it drops every 10th request, and now I'm seeing a lot of broken pages. If only you'd written an offline-first PWA and used background-loading and prefetching with some intelligence to retry failed requests in order to deliver content before I actually need it this would have worked so much better.
But hey, the app is perfect for you on your robust wifi, so it must work out there in the real world, right?
Snark aside, there's a good reason to build web software in a variety of different ways. Pick the one that suits your users. Don't assume everything works perfectly and every internet connection is strong, fast wifi. That just means a lot of users (especially poorer ones in rural communities) are going to struggle with what you're selling.
Everything is a tradeoff.
Assuming you aren't one of the sites that thinks it's appropriate to require `DOOM.WAD`, it can work out well.
> it drops every 10th request
If the app saves data to a server, that's gonna happen regardless anyways (and one could argue that techniques like optimistic updates might make the experience even worse)
Also remember that HTML can be streamed and partially rendered even if it's not fully downloaded, and the investment required to enable this is minimal. The same cannot be said about a SPA's data fetching/rendering strategies.
Having worked on bloated apps before, I despise when people throw around the word "prefetching" as if it's some sort of magic spell. At best it eats my battery and bandwidth needlessly, and at worst, it's weasel wording for slowing down time-to-first-pixel :(
I think that's pretty damn good; to solve the 2 second latency, I think the developer is better off using a CDN or Netlify's ADN so that your site is delivered from a server closer to the user.
Terrible connections exist. But I fail to see how adding more scripting will always help with that. Service workers are definitely promising, but documentation for common use cases is still lacking.
Even with a terrible connection, I would still trust a server-side app to load correctly on a refresh more than a SPA.
If anything, the minimal use of Javascript is as inclusive as a web app can be.
Your app has no control over browser connection management. JavaScript you write is aware of idempotency and retry semantics of your app, so it can manage requests more intelligently than the browser, without disrupting the user experience.
So sure, it won't always help if you do it badly, I can see how it always helps if you do it well.
That said, even though the way service workers are registered is sort of odd, they're pretty trivial to work with once you're there. As far as resources go, MDN is always pretty solid, and this post (https://developers.google.com/web/fundamentals/primers/servi...) on Google's web dev guides is what I used to familiarize myself with them.
Then I would have waited too long to load the page the first time and give up.
Not enough developers build this way yet though because it's quite hard work. In my opinion the answer is to use what browsers are capable of; it should not be to ignore the browser and do as much as possible on the server.
Who has good heuristics for retry (or other techniques) to achieve reliable communications for a PWA?
We do retries, with a few different strategies, but we have a poor idea of exactly how effective (or not) our retries are. Also my code for this is ugly and it's a nasty area to test properly...
I often work on a train over a 4G connection, so this describes me too. I much prefer traditional style web applications over SPAs specifically because of this.
You know what happens if a request is taking too long or fails with a traditional web app? I hit reload and it retries.
You know what happens if the same thing happens with most SPAs? It throws away my unsaved data and puts me back on the starting screen.
Sometimes there's a JavaScript-powered retry I can use instead. Sometimes it works, usually it is very unreliable (if something has failed to load, often the JavaScript is having problems too).
Sure, I'm willing to accept the possibility that an SPA can be written in such a manner as to compensate for this hostile environment. It's just: I've used a lot of these apps, and I haven't found one that works reliably over a poor connection yet.
This is just bad design though. Obviously the dust isn't settled on the whole html5 technology yet, but for anyone who is wondering how to make an SPA more resilient to network instability, the solutions are: axios-retry, idempoteny PATCH/PUT, service workers, basic error handling, and always have the URL represent where the user is.
Thing is, you get proper error messages, the ability to retry, warnings if you're about to repeat an unsafe operation etc out of the box if you don't use a SPA and render things server-side. The browser does this for you.
Sure, you can do a better job. But I've lost count of the number of SPAs and JS-driven apps where connection error = infinite spinning loader icon and no feedback or ability to retry. If you're going to willingly throw away the routing you get from HTTP, the progress indicators you get from actual links and non-AJAX requests and the browser error handling you better make damn sure you implement it all again in JS and provide feature parity for the user.
It's not just HTTP either - accessibility features like keyboard navigation often aren't implemented in React components because folks only write an `onClick` handler. Sure - the devs should've loaded "eslint-plugin-jsx-a11y " and enforced rules that prevent this sloppiness on their CI - but it's far too easy to do it wrong and so devs do do it wrong and the users suffer from a poor experience as a result.
I have an opinion too. But my opinion and your opinion really are moot.
Because do you know whose opinion is probably backed by the most data and the most expertise? Google’s Gmail team.
And Google, with all its amazing developers, who are arguably at the forefront of javascript standards and general web app innovation, and who have literal billions of visits, they have made the decision to give you a link and tells you to use a basic, server-side rendered web app if you have a “slow connection.” And they do this while attempting to boot up all the PWA/SPA framing of the main app, just in case, I don’t know, all that stuff fails.
So I think I’m going to give Gmails opinion a lot of weight here
I completely agree with the point you're making though; if a user is on a low bandwidth connection then sending as little data as possible is best, and that's likely to be plain HTML with very little rich content like images.
That said, Gmail's slow connection UI is horrible to actually use. Very few people choose it over normal Gmail. Most people want the big fancy app, so we as developers should work out the best way to deliver that.
This is a unicorn. If it's the real world we're talking about, as far as I can tell client-heavy webapps that are actually engineered this well -- and don't frequently end up in a more inscrutable state than a pageload failure when network issues rear up -- are rare enough in practice that it feels weird to hold that up as the yardstick for criticism.
And if the point is that more applications should get there, well, that's more or less orthogonal to the point of the article, which (in spite of the headline) isn't so much that no one should use JS for anything ever (since the author details the JS he made a thoughtful choice to include), but that a notable range of UI behaviors that are frequently implemented using JS without a second thought don't necessarily have to be, and there are plausible benefits to the alternative. So this isn't exclusive with background loading / prefetching.
As to interpreted languages, JS/V8 has probably seen more optimization than any other interpreted language. As to Java, you don't need a 400-500mb base image either. Rust takes much longer to develop against, as does erlang even if you're familiar with it. If you're parsing JSON into concrete objects, the overhead is often larger with compiled languages than interpreted, where JS just works. I can generally get stuff working in around 1/8 the time it takes for peers to use a compiled language to do the same.
Go is probably a better option than the three you suggested, even then more often than not, I would choose node, just because of the flexibility I get with npm and the larger community. If I, in turn need to eek more performance, or have a process that requires more compute per request, then I'll often reach for another language and/or even choose a Function as a Service wrapper.
No one seems to get that the C++ still works and in nearly all cases is faster, compiles very fast and is very quick to develop with now that everyone has shared their code and since functional algos have been added to the stl. This all happens with the memory safety people require since we have shared and unique pointers and Intel's thread building blocks. My fully featured application is 3.7 MB and takes 12-15 MB of ram to run and handles 100k+ requests per second from my laptop. Those stats aren't even that good with the exception of the rps.
If going fully native isn't your game then how about nginx with the plethora of 3rd party modules that are supplied by other developers? Maybe you just really like js? Maybe js isn't the best but you're going to use it anyway because you know it and other people have already written a lot of your code for you? Maybe you just feel like wasting money on more machines instead of one vps instance and a cache-everything cdn?
You do you I guess.
I just said that JS isn't so bad, and that language snobbery is worse when it means you're far less productive.
Use the tool / language that makes the most sense for your problem.
Assembly language backends on the other hand absolutely scream.
And with a modern IDE they're a joy to develop.
Most of this stuff isn't new, it's about a decade+ old at this point. Backend developers often tend to turn up their noses on web front ends, so they don't bother to actually learn anything meaningful, and just sit complaining constantly.
https://caniuse.com/#feat=details
And here I thought Edge was a modern browser.
If anything, this is the works everywheriest of things that "work everywhere" in post-2010 web development. Some other stuff actually breaks accessibility by default.
The key is to represent your data and UI state via sane HTML. This article demonstrates this well with the modal popup example. Once you represent popup's state with a checkbox, you can manipulate that checkbox in any way you want and you can style it in any way you want and your styling will not have any dependencies on the methods of manipulation.
I don't mind projects which use a tiny bit of hand-coded, vanilla ES6. Would be nice if there were a slogan that included these.
Looks like it actually does use JavaScript for the "select2" case. This is exactly the case I'd be happy to special case.
edit: added paragraph
[1] https://m.signalvnoise.com/stimulus-1-0-a-modest-javascript-...
It's got a badge and everything.
https://www.fontsquirrel.com/tools/webfont-generator
I agree that it's a hack and I can't say that I like it. Blame the W3C and browser vendors for providing incomplete/barely functional standards like <dialog>.
Thanks and keep up the good fight.
Was this intentional? It gave me a bit of a chuckle.
- storing states, and conditionally passing states around
- toggling things on or off
- element reuse
- dealing with events
- ajax
Unfortunately it's unlikely HTML & CSS would support all these.
Behavioral augmentation that these days is encapsulated via componentization (e.g. tabs, carousels) is also something that has been explored to death in traditional paradigms, e.g. unobtrusive js
And there are so many edge cases popping up so that you have to use JS anyway. Then you may ask why don't I use JS for everything then?
JS is inevitable no matter how you and I hate it. "JS-Free" only makes sense for simple presentations.
To me, this whole thing about JS-Free is a backlash against overdoing things in JS, and it's as good time as any to rethink today's status quo, especially in regards to how many babies we have thrown out w/ the bath water when adopting JS-first technologies.
I noticed on the invoice page, on mobile, the number inputs (qty, price, etc) showed the full alpha keyboard on input. I think you can add a few attributes to optimize, without using JS also.
Great work, thanks for sharing.
This link has been helpful for me:
https://developers.google.com/web/fundamentals/design-and-ux...
Definitely a few hacks involved. I’ve seen different combinations of using pattern, type=number, step=“0.01” etc, but you may need support for longer decimals in qty/price.
I’m always on the lookout for form best practices, but it’s definitely hard to stay up with all of the current trends.
There are a few mobile preview tools, not sure which is currently best, for testing on different virtual devices:
Google search: test site with iphone online
but there are a lot of use cases where js is essential.
it all comes down to what you’re building.
Absolutely, and a ton of commenters (mostly on Reddit) seem to be missing that point. Use the right tool for the job. In the article I explicitly state that I couldn't have built the drag-and-drop invoice editor without JavaScript (although I wish that I could!).
Of course, then you need a full page reload every time you edit something.
A lot of things are performed on the server, and then the server responds with a "flash" which is basically a little bar that says "Data saved." or "There was an error: ...".
There are a few places where I used a simple 5-liner to validate a field or something.
Unfortunately there was one place where I needed some heavier JS (for which I used Mithril.js), but it was absolutely required for when you're editing an invoice. You wouldn't want to edit line items and wait for a server response every time you make one change, so I simply determined that this was an area where I had no choice. Mithril simply modifies the DOM inside the <form> element, and then saving is a plain old POST request when you submit the form after you've made all of your changes.
The unfortunate reality is that I couldn't completely free myself from JavaScript, but got a lot closer.