Answer: "Maybe your single page app is different, but the ones that I know break most of my browser’s features, such as the back and forward buttons, page refresh, bookmarking, sending a link, or opening a link in a new window or tab."
None of the things described here are inherent to single page apps. Ember (with Ember Data and proper routing), for example, solves most of the issues he brings up (back button not working, failure to link to individual items) by convention.
The downside of Ember (at least the last time I used it, several years ago) is you must do things "the Ember way", which isn't inherently bad, but there's a lot of implicit functionality that "just works" until it doesn't, and can make debugging a painful experience.
I think so, if only because the discussion should be surrounding best practices and following standards, as opposed to a sensationalist notion that single page apps should be abandoned in general.
I really hate articles that have a large fixed header at the top, that causes the next few lines of text to be hidden when I hit page down. Something something glass houses and throwing stones.
> Modern users want content delivered to them in a personal, rapid, contextual(stateful) manner
Is that actually true? Maybe it's that web developers want to deliver content in this way and try to do it with the browser instead of a native application more fit for purpose and they don't actually give a damn about what the user wants.
> Is that actually true? Maybe it's that wev developers want to deliver content in this way and try to do it with the browser instead of a native application more fit for purpose and they don't actually give a damn about what the user wants.
Remember old forums that made you load each and every reply in a thread?
Compare that to Reddit which allows for collapsing and expanding of threads, and for async loading of reply chains that go on for too long.
Being able to open-in-place media users link to was a huge reason for the uptake of Reddit Enhancement Suite, now, a feature built into Reddit. Upvoting not taking users to a new page or reloading the page (compare to how Slashdot used to do it!), all these features involve maintaining state.
Same thing for most bug tracking apps. If I am triaging a list of bugs, a SPA is the way to go, I don't want to spend more time on page loads then on marking triage status.
- Every click is a full page load, and you still didn't manage to get caching right, so it takes 2 seconds to load.
- When I have multiple tabs open, your badges and notifications fall out of date, because you never poll for updates.
- If I mis-enter information into a form, your error handling forgets all my information and I have to re-enter it.
- You forgot to update the styles on that rarely-used page, and now it's got half the new CSS and half the old CSS and the point is the header runs halfway down the goddamn content in Times New Roman.
I get it, there are problems with SPAs. But this perfect land of one-page-per-link has real usability problems if it's not implemented properly, and there's a lot of maintenance overhead associated with a sprawling mass of transcluded templates. If we're going to criticize an architecture because it's easy to poorly implement, I don't think "ROCA" or whatever we're calling it has any meaningful benefits over SPAs in the general case.
"If I mis-enter information into a form, your error handling forgets all my information and I have to re-enter it."
This. I have a very short list of architectural must-haves that I use when evaluating junior dev's code at the start of a project, and not having robust error handling that preserves submitted form information is right up near the top.
It's an annoying design mistake with two input elements, unworkable with five, and just plain cruel beyond that.
Just handing someone an expanded version of "implement good error handling" is not likely to result in a good product. Every project has nuance and subtle trade-offs -- the code review is an opportunity to teach juniors how to map abstract design principles onto concrete implementations without falling back to "cookbook / cargo cult" programming.
I want them to take ownership of their own code, and advance down the path of craftsmanship because I believe this results in long-term value -- both to them personally and to the project at hand.
You're right! If we just implemented everything correctly, there wouldn't be problems!
Anecdotally, every team I've worked with has converted to a SPA, and the main ergonomic upgrade is that updates to the signup page or the "not found" widget quit getting forgotten whenever we change tweak our design. There always seems to be one page that's out-of-date for 2 months before anybody notices, then it quietly gets fixed up because we forgot that the design tweak was a CSS+HTML change, not pure CSS.
Not that that's perfect -- once you have a SPA, nobody remembers the 404 page, because it's a different HTML base...
I only scanned towards the end, but I was waiting to here what his proper architectural would be. I don't like complaining unless you have alternative you're ready to support.
From TFA: "What is this alternative, then? It is to build a classic web application, including rendering server-side HTML, and use JavaScript only sparingly, to enhance browser functionality where possible."
- The author is describing hypertext, which is perfectly fine for textual static content, one of the key goals for which the World Wide Web was conceived.
- Server-side generation of HTML seems totally out of place for other use cases: HTML/JS are now the assembly languages of the web and they belong in the browser. Besides a more distributed architecture is just perfect: server and client focus on what they do best.
- Fixing web-apps with broken history buttons and unlinkable urls is important, but to be honest there are fewer and fewer SPAs that are mono-url these days. And it's perfectly fine to have a unlinkable "Delete this Invoice" app button. If it's not real content it should not be a link.
- If I mis-enter information into a form, your error handling forgets all my information and I have to re-enter it.
Even when I was coding web pages in Perl in the 90's I wouldn't do that to a user, that's just mean. You don't need a SPA to provide proper error handling that also rebuilds previous state.
Oh, I know, I have a few examples as well, mostly regarding paying bills. Hate those sites but what can I do? I guess I could go back to mailing a check :-)
> If I mis-enter information into a form, your error handling forgets all my information and I have to re-enter it.
This really shouldn't be a problem for anything other than file inputs. If someone's form is losing submitted information on a validation failure when using an actual HTML submit, then I find it pretty unlikely that they're doing a good job of AJAX submission.
You're basing a lot of this viewpoint on fast internet connections. Javascript code rendering will be faster if you have a slow internet connection and are using an updated browser with a fast javascript engine
In theory you have a point. In practice though the initial loading of the first page is so slow that it completely negates the benefits entirely.
It also doesn't work well when opening lots of tabs (which is a popular technique that helps immensely when browsing on a slow connection as pages are loaded in the background).
And if you have a really slow or spotty connection the javascript page would barely load at all.
I think you're basing this off of old technology. With webpack 2 and HTTP/2, this is less of an issue. But, the top comment's statement I think is the most valid: if you're loading static text, then simple html pages are the best. Notice the rise in popularity of static site generators for this purpose. A SPA will win out when you have lots of interactions and you only experience the initial loading of the first page once - and then the majority of your user experience is around interacting with the app.
Eh, I've yet to experience an spa that feels or is faster than the equivalent simple html, no matter what network I'm on - with the exception of being off-line (and I've seen few enough spas that actually work better offline than static pages, ie not broken navigation/functionality).
Note that I've of course seen countless slow, bloated static pages - but that's pretty much never because of the tech stack - but because of bloated css, silly large images, and often horrible convoluted html.
Hn is of course a typical example of a web page that on paper would be great as an spa, but works fine as a static page with minor js (broken up/down links notwithstanding..).
Assuming a 64 kbps connection, and a 50%+ gzip compression, downloading a couple of pages of text is done in about half a second. If the user has such a terrible connection, they've hopefully turned off image loading. I don't think adding 40-400 kbs of gzipped js is going to help with the speed...
How do you know when you're using a SPA and when you're not? Seems to me, a well designed SPA is indistinguishable from a regular HTML site unless you're actively looking at the network tab. Could it be that you have a bad impression of SPAs, because you only really notice it when you're using a badly designed one?
My homepage has about 81kb of data to transfer, including images.
If you turn off images, you get in with 12kb of data. I think 12kb of data should be transferred faster than even the more lighter JS frameworks around...
One nice thing about regular HTML websites is that they only need to be downloaded once. When I was taking the train in to school, I'd often have a bunch of tabs open on my phone. If I tried to switch to an old Reddit (SPA) tab while going through a tunnel, the page would try to reload, fail, and replace all the content that was already there with an error message. With HN, by contrast, I could open up a bunch of tabs while I still had good internet and switch to them later even when I didn't.
From time to time I have to use 64kbps on my cellphone. At that point the biggest pain in the ass becomes first loading and in that classic HTML app shines, it loads a LOT faster - if you have 1.5MB page or 600kB makes a huge difference. But once it's all static content is cached it pretty much doesn't matter if it's XHR or full page load since I'm loading only tens of kBs at max.
Also if I have to use 64kb I completly disable js in my browser and whitelist just a couple of websites - best adblock on mobile.
i much rather see my html
page slowly loading before me than a js button that doesn't load and when i decide that it's taking too long and i want to go back then everything just messes up because it doesn't work with the back button in the browser
Since we're talking about poorly implemented features.
Why I hate your SPA:
- If I click on a link, I receive no indication that I actually clicked a link, then 2-10 seconds later the page abruptly loads. Sometimes the page doesn't load at all.
- I cannot tap and hold a link, wait a second for the pop-up menu, and open the link in a new tab because the link isn't actually a link. It's implemented in JavaScript.
- The back and forward buttons don't work as expected because either history isn't maintained or it's shoddy (scrolling should not be a history event).
- The refresh page button don't work as expected because it resets you to the beginning.
That is absolutely true, but I've seen it enough times to be annoyed. Also, the grand parent post was about classic HTML apps which were poorly designed with issues that should be easily avoidable.
A normal web page doesn't have to be well-designed in these ways, though. And SPAs often are not designed in this way. The author addresses this pretty well in the first paragraph.
I think the prime directive of all UI should be, "Ack UI inputs immediately, no matter what else is going on." Throw up a spinner, make something throb, change a status to "loading", anything. Don't just go off and attempt to do it and hope for timely response.
Why can't the browser do this? I mean, it's a universal function / feature, right? And ultimately, it's the browsers that's waiting, not to application per se. (Yeah, I okay. But you know what I mean).
I can't think of any good reason why the browsers can't help out with this UI / UX. Am I crazy?
There has to be a wait somewhere. That is, the browser can't update anything until it knows what to do. So between the click and that new assignment is wait, yes?
There was some website linked to by HN a while ago that as you scrolled down the page, history events would be recorded. So if you wanted to go back to the previous page, you'd have to press back about 8 times if you scrolled to the end of the article.
When you click a link or button on a normal website, the browser doesn't give indication on the ongoing state of your http request either. Only a javascript-enhanced experience can do that.
Clicking on a save button and seeing the page hang is the normal way browsers handle http requests. But a javascript-enhanced experience can tell users about the local perception of the http request (ongoing, timeout, retry, error), or it can inform the user that it's safe to navigate away while their requests retry.
Most indicate this by making the refresh button into an X or similar, and usually with one or two small visual indicators. Safari on iOS shows a little bar, etc.
Firefox for Android shows a thin orange progress bar beneath the navigation bar, and Chrome for Android shows a similar blue one. Firefox and Chrome for the desktop show the loading spinner in the tab.
I love browser push state so cool, but yeah if it doesn't work right you get stuck not being able to go back.
Edit: also agree to it disappear when accidentally hit back or leave page... I don't think you need to ask permission to use cookies so should opt for it... Or some other way to store.
The problem with using cookies for navigation is then you restrict the user to a single window/tab. If the user opens a separate window and navigates somewhere else in the application, the cookie state will reflect the new window and disregard the state from the previous one.
Sorry for the cookies I meant the storing of whatever you were writing into a form and accidentally hitting back or navigating away from the page. Like in mobile leaving the browser and returning to it, sometimes for me it starts it over haha no, just empty your soul into an essay response, all gone. Probably soared the recipient.
Interesting about the separate window, thought cookies could be set to never expire.
Oh, I see. For saving the form state, localStorage would work better than cookies for saving and restoring because then the information is only kept on the client. That still doesn't solve the multiple window problem, but that's likely less of an issue for a filling out a form than for navigation.
Cookies can certainly be set to never expire, but you don't know which window the cookie was intended for because the cookies are shared.
thanks for the references, I'd like to start using it at some point. Would be cool to check the client's device capabilities/run a specific event based on conditions met.
In about 95% of the SPAs I've tried, each click takes even longer than that.
"you never poll for updates"
Yes, because apparently AJAX/AJAJ is completely nonexistent and there's absolutely zero ground between "pure-HTML like it's 1991" and "VueWangulaReact.js single-page monstrosity".
Hell, even a meta-refresh will fix that if you really do want "pure-HTML like it's 1991", though that's certainly inelegant.
. - If I mis-enter information into a form, your error handling forgets all my information and I have to re-enter it.
This was fixed in browsers years ago when they started keeping form state along with page history. I can refresh this page, go back and forth in history, and this text box will keep my comment. Ironically, websites that tried to be clever about form state management and faked page transfers (like ASP.NET WebForms) didn't benefit from the fixes and currently are significantly worse off in terms of usability than websites that used vanilla forms.
. - If I mis-enter information into a form, your error handling forgets all my information and I have to re-enter it.
Funny that you mention it. Loosing form data on page refresh or clicking "back" button was a problem on the Web until browsers started keeping form state along with page history. Suddenly millions of websites got this new feature without actually doing anything differently. But that was only possible because page transfers were semantic. Frameworks of that time that tried to manage their own form state (like ASP.NET WebForms) didn't have real page transfers, so they didn't get this "upgrade" and currently are significantly worse off in terms of usability.
The last bullet is just a straight up error on the part of the developer. The third... isn't real? What browser doesn't save form state?
The first two are fair criticism but they aren't nearly as detrimental to the user experience as the typical single page app antipatterns (no history, no indication of page loading, refresh button is broken, etc).
While it is of course possible to execute any design well or poorly, the observation that a typical single page app is worse than a typical multipage app is certainly valid.
Some of this hits home for me. I work on the WAY DEEP backend of one of these 'modern single page apps', so dont really care about the REST apis or UI/UX. I will say our actual website is a nightmare to use and I attribute a lot of it to (A) mgmt demanding bad UI/UX on short timeframes(B) front end folks having the power to write butt loads of JS code to achieve said goals with no thought to system wide architecture / integration. We have so much shit happening each time you click to a different part of webapp, we seem to have hit the messy phase of a codebase where you cant change one part of the site without breaking something else in a totally different section of the codebase.
to be fair though, as with everything there are pros and cons. Once i learned to deal with some of complexities and crazy UI/UX, using our web app is like driving a spaceship and it does work pretty fast considering all the data getting lifted, html getting rendered etc.
"you cant change one part of the site without breaking something else in a totally different section of the codebase"
Welcome to the feeling I had in 2005 in a Java swing "application".
And I guess lots of people felt the same even before..
but that's the point: an application isn't a webpage. An app doesn't have a concept of back and forward, it has undo & redo. It doesn't need refreshing because it's syncing with the server by websocket. It doesn't have location it has state.
One big downside of SPAs the author didn't mention is the chain reaction effect I wrote about last year[1], where if you start to implement one browser-native feature in JS, it breaks other browser-native features, and it begins a chain reaction where you basically implement the whole site in JavaScript, and the result ends up being noticeably worse than if you had just stuck with native browser features from the beginning. It's not necessarily specific to SPAs, but it's definitely a very common problem I see with them.
> Maybe your single page app is different, but the ones that I know break most of my browser’s features, such as the back and forward buttons, page refresh, bookmarking, sending a link, or opening a link in a new window or tab.
That reminds me: if anyone out there has any advice how to make AppCache play nice with JavaScript-based routing solutions (say, react-router), please let me know.
Are you using AppCache for compatibility reasons? If yes, my condolences. If no, just use Service Worker. If you don't want to write it yourself, use sw-precache.
They're all manageable and can be tidied up behind the scenes.
The beef seems to have a smack of functional fixedness.
Wants their browser to be used the way it was originally designed back in Netscape 1.0 days. "The web can't evolve or its not the web!" as I see it
I see this a lot in the 35+ tech crowd these days. It's like the 50+ tech crowd shaking their canes over terminal emulators or using anything other than Perl for sysadmin.
The biggest things for me are probably the excessive amount of resources used, the long load times, how hard it is to get things "right" (like not breaking the back button) and links taking awhile to load because we have to load your stupid SPA (if they work at all).
SPAs did win out big in one area though. On mobile. Except on mobile they're called "apps". As much as many HNers like to chafe against native mobile development as they yearn for the open standards of the Web, native apps have basically "won". Users love em.
I guess that leaves desktop.
I do wish more people asked "does my Web presence need to be an SPA?" or even "will it benefit significantly from being an SPA?" because I feel like the answer more often than not is "no".
How hard it is to get things right? It's not hard at all. It's builtin with frameworks like Ember or Angular, and for React you got react-router which does things correctly by default. It's just bad programming. I don't even understand why people would make an SPA without a url-router. Doesn't that just make development harder/more anoying?
Why would links take longer to load in a SPA? CSS, Javascript and the like should already be downloaded, there should be much less networking involved.
Also. My SPA's come in at around 120-130kb minified (no gzip). That's about the same as "pure-html" sites which include jquery.
I do agree with your last point though. Pure content sites, or even "mostly"-content sites, is probably better off with backend generated html.
I wouldn't say that mobile apps "won" anything, just that networks and hardware aren't fast enough yet for browser on mobile. Desktop started the same way, and just as most of the desktop apps have moved to the browser, so too will mobile apps. React native, cordova etc are just the early signs of this transition.
Most websites get little benefit from using a SPA architecture, and the negative aspects are significant:
- deploy headaches
- the infamous JS fatigue
- debugging (minified code and nested anonymous functions make it painful)
- troubles with content blockers
I guess we'll have to wait a few years until the JS/SPA ecosystem will become more mature and find good solutions to these annoyances. Until then I'd gladly stick with rails, django & friends, both as a developer and as a user.
Use a decent router. Single Page App ecosystems have been festered by mediocre routers that make doing stuff like this hard, where there's no need to be.
This is the design our team likes to follow, I haven't seen it documented anywhere (but I doubt it's new), so I'll write it here.
Essentially, we try to replicate traditional server side MVC thinking on the client side. Decent server side rendered apps that don't have the problems mentioned in the OP's post usually follow RESTful principles to some extent. This means that what you see on a page is a pure (ish) function of these things:
* The parameters in the URL
* The state of the database
And nothing else. Translated to the client side, it means that you want anything the user sees to be a function of:
* The URL
* The state of the model layer (or the flux stores, whatever)
* Addendum: 100% of the model layer is an
eventually-consistent subset of the database
We use one exception to this rule: unimportant state is allowed to be right in the view (we use React, so that's component state). Stuff like "is the dropdown menu expanded" or "which of the items in a list are selected" is neither in the URL nor in the model layer (because we won't sync it back to the database). The rule of thumb is "if the user refreshes the page, is it a problem if this data is lost?". If the answer is "no", we can make it component state, as close to the action as possible (so not in the root component usually).
All of this combined gets us a lot of stuff for free. For example, all our modal dialogs, and even "is the menu shown" is addressable from the URL. This might feel like overengineering, but it gets us lots of stuff for free.
For example, users on mobile phones expect to be able to close a modal or a menu by hitting the hardware back button. We get this for free, because when the user does an action that causes a modal to show, we redirect to some URL like example.com/wherever/?someModal=1. (all using the history api). Router picks it up, modal is shown. Model layer is not touched, this is 100% view layer work. Then, when the user hits "back", the browser restores the URL to example.com/wherever, router picks up the change again and the view renders the same page but without the modal.
We get all the usual browser features for free simply by following basic REST ideas. Don't put URL stuff in your stores. Don't put non-backend-synced stuff in your stores (just like you wouldn't put important state in your sessions in server side rendered apps). Put everything that matters in the URL.
Any routing library that encourages you to sync URL stuff into your model layer is, IMO, wrong by design.
I half way agree here. Too many things are SPA's that don't need to be. An internal tool used by < 1000 people? Maybe let that stay server side rendered. A cutting edge app that promises a new clean way to bank/farm/hire/cryptowhatever, then please embrace SPA's and PWA's. The reason we have a glut of these SPA's is because we have devs, designers and product not recognizing context. Some things don't need to look or feel cutting edge. The real tragedy here is that I've seen companies burn thousands on SPA's when they could have had one front-end on their API team make it server side rendered.
> Maybe your single page app is different, but the ones that I know break most of my browser’s features, such as the back and forward buttons, page refresh, bookmarking, sending a link, or opening a link in a new window or tab.
Each screen has its own discrete URL, so you can share links, refresh, bookmark or open in a new tab, just like any other website.
I think the criticism here is misguided. The problem is not with SPA as an architecture, it's with SPA devs not implementing features that end users are accustomed to on a website. They can all be added, it just takes effort.
The biggest valid criticism to SPAs IMHO is the initial site load. There are ways to mitigate (CDNs, progressive loading, compression, etc) but pound-for-pound SPAs probably have longer initial load times on average for the first page than a vanilla HTML site does.
Once it's loaded there's no faster way to traverse a website, IMHO.
Middle clicking works great, so I am happy! Mice need at least 5 buttons anyway.
Right click and open in new tab also works. I always forget about ctrl-click since I never use it, odd that it doesn't work given how well everything else does!
That is an incredible website. It is amazingly responsive and fast, I couldn't tell it was an SPA because of how well it behaves in every possible way, yet it is far too responsive to be anything but an SPA.
Even as someone who cooks a lot, using the site doesn't come naturally to me, my typical shopping method is to go to the store and think up of what I want to cook as I stroll around the outside picking up fresh food. Or I put recipes from blogs into a shared family OneNote section and use that to do more directed shopping.
That said, the links off of the food pages are high quality, with very good info on what to do with each item, so I try and spend some time exploring the resources you've put together!
Broken for me. I am middle clicking to open in a new tab and Javascript is forcing it to open on the current page. Additionally, the page is blank except for a background image when Javascript is turned off.
The problem arguably is with SPA as an architecture.
The problem is not that SPA devs are not implementing these features, it's that the devs are responsible for implementing them in the first place. As other people have noted, even though you have gone through great lengths to make your site work as expected, "open in new tab" does not work.
These are browser features, and devs shouldn't have to worry about implementing them.
It's an incredible website! But it still does not work completely "right." For example, the back and forth buttons don't navigate to the correct scroll locations.
This actually illustrates his points. Which is this: the browser(s) ALREADY implement all of those features. It already knows forward and backward and new tabs and link and etc. etc. etc. SPA's completely ignore these and must re-implement everything. This means:
* choppy differences between different SPA
* when new browsers come out with new navigation features, guess what, you have to reimplement them!
* Extra javascript code
* Extra bug opportunities
* What if a user keeps your page open so long that you change JSON? I have seen my wife keep a tab opened for weeks....
I guess this is just my DRY training kicks into overdrive, but reimplementation of well-tested, well-proven patterns really doesn't smell right. Which was his point.
Your pages load quickly in Chrome, but not in Safari or Firefox.
The back button normally preserves scroll position. Your site takes me back to the top of the list.
History transitions are especially jarring in Safari. It optimistically restores the viewport state, so at first the page appears to be loaded but doesn't respond to clicking or scrolling. Click events register before the page is repainted, so the user can end up on the wrong page. After that, the list appears to jump to the top.
As other people have mentioned, some ways of opening a new tab work, and others don't.
> Once it's loaded there's no faster way to traverse a website, IMHO.
In my experience, this is almost never true. SPAs are good for some kinds of interaction, but traversing collections of resources is what web browsers are designed to do, and they do it pretty well.
The author's criticism is that people often build SPAs based on theoretical benefits and don't recognize or don't put in the effort to fix the practical shortcomings.
He makes some good points. There are good reasons and uses of the tech in SPAs, for example, I show previews of documents made with forms by hiding the form and rendering the document, but on a blog app I made where I did the much the same thing I made a button that linked to the article so users could find it easily. The "go back" button didn't work to reopen it though and I suspect search engines wouldn't have known what to do with it.
Lot of SPAs I have seen also reinvent the desktop. They have their own tabs and dashboards. And they do it badly - no shortcuts, no integration with the file system..
Stefan identifies common pitfalls or shortcomings of most single page applications such as:
1. Bad performance because of too many operations
2. Breaking browser navigation buttons
3. Not having unique links/anchors to in-page content.
While there are in fact multiple ways of bringing focus and solving these problems the authors suggests that simply ditching SPAs for native toolkits or backend/frontend system is the way to go.
While switching away from SPAs might mitigate the problems listed above - it will also present new problems that developers have to tackle - or simply spend time on.
The main problem here is how hard the problems he describes are to deal with in current frameworks I believe.
He has one thing 100% right - bringing attention to those problems is the right way to go.
127 comments
[ 2.7 ms ] story [ 202 ms ] threadDoes it matter though, if they are still all prevalent on single page apps?
Modern users want content delivered to them in a personal, rapid, contextual(stateful) manner
Fight.
Is that actually true? Maybe it's that web developers want to deliver content in this way and try to do it with the browser instead of a native application more fit for purpose and they don't actually give a damn about what the user wants.
Remember old forums that made you load each and every reply in a thread?
Compare that to Reddit which allows for collapsing and expanding of threads, and for async loading of reply chains that go on for too long.
Being able to open-in-place media users link to was a huge reason for the uptake of Reddit Enhancement Suite, now, a feature built into Reddit. Upvoting not taking users to a new page or reloading the page (compare to how Slashdot used to do it!), all these features involve maintaining state.
Same thing for most bug tracking apps. If I am triaging a list of bugs, a SPA is the way to go, I don't want to spend more time on page loads then on marking triage status.
- Every click is a full page load, and you still didn't manage to get caching right, so it takes 2 seconds to load.
- When I have multiple tabs open, your badges and notifications fall out of date, because you never poll for updates.
- If I mis-enter information into a form, your error handling forgets all my information and I have to re-enter it.
- You forgot to update the styles on that rarely-used page, and now it's got half the new CSS and half the old CSS and the point is the header runs halfway down the goddamn content in Times New Roman.
I get it, there are problems with SPAs. But this perfect land of one-page-per-link has real usability problems if it's not implemented properly, and there's a lot of maintenance overhead associated with a sprawling mass of transcluded templates. If we're going to criticize an architecture because it's easy to poorly implement, I don't think "ROCA" or whatever we're calling it has any meaningful benefits over SPAs in the general case.
This. I have a very short list of architectural must-haves that I use when evaluating junior dev's code at the start of a project, and not having robust error handling that preserves submitted form information is right up near the top.
It's an annoying design mistake with two input elements, unworkable with five, and just plain cruel beyond that.
"Hey, here is your test and here are the answers. You've got one hour" :)
Just handing someone an expanded version of "implement good error handling" is not likely to result in a good product. Every project has nuance and subtle trade-offs -- the code review is an opportunity to teach juniors how to map abstract design principles onto concrete implementations without falling back to "cookbook / cargo cult" programming.
I want them to take ownership of their own code, and advance down the path of craftsmanship because I believe this results in long-term value -- both to them personally and to the project at hand.
> You forgot to update the styles on that rarely-used page
That's solved with proper css, not SPA.
Anecdotally, every team I've worked with has converted to a SPA, and the main ergonomic upgrade is that updates to the signup page or the "not found" widget quit getting forgotten whenever we change tweak our design. There always seems to be one page that's out-of-date for 2 months before anybody notices, then it quietly gets fixed up because we forgot that the design tweak was a CSS+HTML change, not pure CSS.
Not that that's perfect -- once you have a SPA, nobody remembers the 404 page, because it's a different HTML base...
How bad is that really if users are aware of it and they can simply reload the page?
Of course, chat/messenger is something else, but I consider that an exception. (It's easy to build a chat service on top of a static website).
- The author is describing hypertext, which is perfectly fine for textual static content, one of the key goals for which the World Wide Web was conceived.
- Server-side generation of HTML seems totally out of place for other use cases: HTML/JS are now the assembly languages of the web and they belong in the browser. Besides a more distributed architecture is just perfect: server and client focus on what they do best.
- Fixing web-apps with broken history buttons and unlinkable urls is important, but to be honest there are fewer and fewer SPAs that are mono-url these days. And it's perfectly fine to have a unlinkable "Delete this Invoice" app button. If it's not real content it should not be a link.
Even when I was coding web pages in Perl in the 90's I wouldn't do that to a user, that's just mean. You don't need a SPA to provide proper error handling that also rebuilds previous state.
In fact sometimes their web form just forgets things for no reason.
There is no competitor in sight because of the huge huge SEO juice they have built over 15 years.
But it's not THAT inconvenient!
This really shouldn't be a problem for anything other than file inputs. If someone's form is losing submitted information on a validation failure when using an actual HTML submit, then I find it pretty unlikely that they're doing a good job of AJAX submission.
1. Every click is fetched and rendered faster than your javascript code has had a chance to execute, let alone fetch or render.
2. True, minor headache though compared to the vast improvements in usability.
3. Nope.
4. Nope, this is a bug - something javascript pages most certainly are not immune against.
It also doesn't work well when opening lots of tabs (which is a popular technique that helps immensely when browsing on a slow connection as pages are loaded in the background).
And if you have a really slow or spotty connection the javascript page would barely load at all.
Note that I've of course seen countless slow, bloated static pages - but that's pretty much never because of the tech stack - but because of bloated css, silly large images, and often horrible convoluted html.
Hn is of course a typical example of a web page that on paper would be great as an spa, but works fine as a static page with minor js (broken up/down links notwithstanding..).
D-lang forums is another :
https://forum.dlang.org/
Assuming a 64 kbps connection, and a 50%+ gzip compression, downloading a couple of pages of text is done in about half a second. If the user has such a terrible connection, they've hopefully turned off image loading. I don't think adding 40-400 kbs of gzipped js is going to help with the speed...
https://gist.github.com/Restuta/cda69e50a853aa64912d
If you turn off images, you get in with 12kb of data. I think 12kb of data should be transferred faster than even the more lighter JS frameworks around...
Unfortunately connection are more intermittent now than ever before.
Also if I have to use 64kb I completly disable js in my browser and whitelist just a couple of websites - best adblock on mobile.
Why I hate your SPA:
- If I click on a link, I receive no indication that I actually clicked a link, then 2-10 seconds later the page abruptly loads. Sometimes the page doesn't load at all.
- I cannot tap and hold a link, wait a second for the pop-up menu, and open the link in a new tab because the link isn't actually a link. It's implemented in JavaScript.
- The back and forward buttons don't work as expected because either history isn't maintained or it's shoddy (scrolling should not be a history event).
- The refresh page button don't work as expected because it resets you to the beginning.
I can't think of any good reason why the browsers can't help out with this UI / UX. Am I crazy?
Clicking on a save button and seeing the page hang is the normal way browsers handle http requests. But a javascript-enhanced experience can tell users about the local perception of the http request (ongoing, timeout, retry, error), or it can inform the user that it's safe to navigate away while their requests retry.
Edit: also agree to it disappear when accidentally hit back or leave page... I don't think you need to ask permission to use cookies so should opt for it... Or some other way to store.
Interesting about the separate window, thought cookies could be set to never expire.
Cookies can certainly be set to never expire, but you don't know which window the cookie was intended for because the cookies are shared.
[1]: https://developer.mozilla.org/en-US/docs/Web/API/Window/loca...
[2]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage...
I finally used memcache(d) so that's cool.
In about 95% of the SPAs I've tried, each click takes even longer than that.
"you never poll for updates"
Yes, because apparently AJAX/AJAJ is completely nonexistent and there's absolutely zero ground between "pure-HTML like it's 1991" and "VueWangulaReact.js single-page monstrosity".
Hell, even a meta-refresh will fix that if you really do want "pure-HTML like it's 1991", though that's certainly inelegant.
This was fixed in browsers years ago when they started keeping form state along with page history. I can refresh this page, go back and forth in history, and this text box will keep my comment. Ironically, websites that tried to be clever about form state management and faked page transfers (like ASP.NET WebForms) didn't benefit from the fixes and currently are significantly worse off in terms of usability than websites that used vanilla forms.
I think there is a lesson here.
Funny that you mention it. Loosing form data on page refresh or clicking "back" button was a problem on the Web until browsers started keeping form state along with page history. Suddenly millions of websites got this new feature without actually doing anything differently. But that was only possible because page transfers were semantic. Frameworks of that time that tried to manage their own form state (like ASP.NET WebForms) didn't have real page transfers, so they didn't get this "upgrade" and currently are significantly worse off in terms of usability.
I think there is a lesson here.
The first two are fair criticism but they aren't nearly as detrimental to the user experience as the typical single page app antipatterns (no history, no indication of page loading, refresh button is broken, etc).
While it is of course possible to execute any design well or poorly, the observation that a typical single page app is worse than a typical multipage app is certainly valid.
to be fair though, as with everything there are pros and cons. Once i learned to deal with some of complexities and crazy UI/UX, using our web app is like driving a spaceship and it does work pretty fast considering all the data getting lifted, html getting rendered etc.
[1]: https://sdegutis.com/blog/2016-03-21-why-i-dont-use-clojures...
That reminds me: if anyone out there has any advice how to make AppCache play nice with JavaScript-based routing solutions (say, react-router), please let me know.
They're all manageable and can be tidied up behind the scenes.
The beef seems to have a smack of functional fixedness.
Wants their browser to be used the way it was originally designed back in Netscape 1.0 days. "The web can't evolve or its not the web!" as I see it
I see this a lot in the 35+ tech crowd these days. It's like the 50+ tech crowd shaking their canes over terminal emulators or using anything other than Perl for sysadmin.
The biggest things for me are probably the excessive amount of resources used, the long load times, how hard it is to get things "right" (like not breaking the back button) and links taking awhile to load because we have to load your stupid SPA (if they work at all).
SPAs did win out big in one area though. On mobile. Except on mobile they're called "apps". As much as many HNers like to chafe against native mobile development as they yearn for the open standards of the Web, native apps have basically "won". Users love em.
I guess that leaves desktop.
I do wish more people asked "does my Web presence need to be an SPA?" or even "will it benefit significantly from being an SPA?" because I feel like the answer more often than not is "no".
You can wrap your SPA in a native app and thus cache the requests ahead of time. But develop on the web, and use Cordova.
Why would links take longer to load in a SPA? CSS, Javascript and the like should already be downloaded, there should be much less networking involved.
Also. My SPA's come in at around 120-130kb minified (no gzip). That's about the same as "pure-html" sites which include jquery.
I do agree with your last point though. Pure content sites, or even "mostly"-content sites, is probably better off with backend generated html.
react-dom is just a touch bigger than that alone, fwiw
- deploy headaches
- the infamous JS fatigue
- debugging (minified code and nested anonymous functions make it painful)
- troubles with content blockers
I guess we'll have to wait a few years until the JS/SPA ecosystem will become more mature and find good solutions to these annoyances. Until then I'd gladly stick with rails, django & friends, both as a developer and as a user.
This is the design our team likes to follow, I haven't seen it documented anywhere (but I doubt it's new), so I'll write it here.
Essentially, we try to replicate traditional server side MVC thinking on the client side. Decent server side rendered apps that don't have the problems mentioned in the OP's post usually follow RESTful principles to some extent. This means that what you see on a page is a pure (ish) function of these things:
And nothing else. Translated to the client side, it means that you want anything the user sees to be a function of: We use one exception to this rule: unimportant state is allowed to be right in the view (we use React, so that's component state). Stuff like "is the dropdown menu expanded" or "which of the items in a list are selected" is neither in the URL nor in the model layer (because we won't sync it back to the database). The rule of thumb is "if the user refreshes the page, is it a problem if this data is lost?". If the answer is "no", we can make it component state, as close to the action as possible (so not in the root component usually).All of this combined gets us a lot of stuff for free. For example, all our modal dialogs, and even "is the menu shown" is addressable from the URL. This might feel like overengineering, but it gets us lots of stuff for free.
For example, users on mobile phones expect to be able to close a modal or a menu by hitting the hardware back button. We get this for free, because when the user does an action that causes a modal to show, we redirect to some URL like example.com/wherever/?someModal=1. (all using the history api). Router picks it up, modal is shown. Model layer is not touched, this is 100% view layer work. Then, when the user hits "back", the browser restores the URL to example.com/wherever, router picks up the change again and the view renders the same page but without the modal.
We get all the usual browser features for free simply by following basic REST ideas. Don't put URL stuff in your stores. Don't put non-backend-synced stuff in your stores (just like you wouldn't put important state in your sessions in server side rendered apps). Put everything that matters in the URL.
Any routing library that encourages you to sync URL stuff into your model layer is, IMO, wrong by design.
My SPA is different - https://www.seasonalfoodguide.org.
Browser back button works like normal.
Each screen has its own discrete URL, so you can share links, refresh, bookmark or open in a new tab, just like any other website.
I think the criticism here is misguided. The problem is not with SPA as an architecture, it's with SPA devs not implementing features that end users are accustomed to on a website. They can all be added, it just takes effort.
The biggest valid criticism to SPAs IMHO is the initial site load. There are ways to mitigate (CDNs, progressive loading, compression, etc) but pound-for-pound SPAs probably have longer initial load times on average for the first page than a vanilla HTML site does.
Once it's loaded there's no faster way to traverse a website, IMHO.
This app is a good example of one of my pet peeves: ctrl-click doesn't reliably open links in a new tab.
Right click and open in new tab also works. I always forget about ctrl-click since I never use it, odd that it doesn't work given how well everything else does!
It doesn't work in Internet Explorer though. No FF on this machine to test out unfortunately.
While the rest functions very well, this is case-in-point ... it's difficult to re-invent app use cases that browser vendors generally take care of.
Edit: Printing is also borked it seems, whether or not that's a use case for your audience is another question.
That is an incredible website. It is amazingly responsive and fast, I couldn't tell it was an SPA because of how well it behaves in every possible way, yet it is far too responsive to be anything but an SPA.
Even as someone who cooks a lot, using the site doesn't come naturally to me, my typical shopping method is to go to the store and think up of what I want to cook as I stroll around the outside picking up fresh food. Or I put recipes from blogs into a shared family OneNote section and use that to do more directed shopping.
That said, the links off of the food pages are high quality, with very good info on what to do with each item, so I try and spend some time exploring the resources you've put together!
This, in combination with the fact that developers don't take the extra effort, is exactly what the article is complaining about.
Only one data point, but 15 seconds to load here. After that, yes, everything is, as advertised, snappy.
The problem is not that SPA devs are not implementing these features, it's that the devs are responsible for implementing them in the first place. As other people have noted, even though you have gone through great lengths to make your site work as expected, "open in new tab" does not work.
These are browser features, and devs shouldn't have to worry about implementing them.
This actually illustrates his points. Which is this: the browser(s) ALREADY implement all of those features. It already knows forward and backward and new tabs and link and etc. etc. etc. SPA's completely ignore these and must re-implement everything. This means: * choppy differences between different SPA * when new browsers come out with new navigation features, guess what, you have to reimplement them! * Extra javascript code * Extra bug opportunities * What if a user keeps your page open so long that you change JSON? I have seen my wife keep a tab opened for weeks....
I guess this is just my DRY training kicks into overdrive, but reimplementation of well-tested, well-proven patterns really doesn't smell right. Which was his point.
The back button normally preserves scroll position. Your site takes me back to the top of the list.
History transitions are especially jarring in Safari. It optimistically restores the viewport state, so at first the page appears to be loaded but doesn't respond to clicking or scrolling. Click events register before the page is repainted, so the user can end up on the wrong page. After that, the list appears to jump to the top.
As other people have mentioned, some ways of opening a new tab work, and others don't.
> Once it's loaded there's no faster way to traverse a website, IMHO.
In my experience, this is almost never true. SPAs are good for some kinds of interaction, but traversing collections of resources is what web browsers are designed to do, and they do it pretty well.
The author's criticism is that people often build SPAs based on theoretical benefits and don't recognize or don't put in the effort to fix the practical shortcomings.
Stefan identifies common pitfalls or shortcomings of most single page applications such as:
1. Bad performance because of too many operations
2. Breaking browser navigation buttons
3. Not having unique links/anchors to in-page content.
While there are in fact multiple ways of bringing focus and solving these problems the authors suggests that simply ditching SPAs for native toolkits or backend/frontend system is the way to go.
While switching away from SPAs might mitigate the problems listed above - it will also present new problems that developers have to tackle - or simply spend time on.
The main problem here is how hard the problems he describes are to deal with in current frameworks I believe. He has one thing 100% right - bringing attention to those problems is the right way to go.