Why you should limit JavaScript — and how to do it

This article was written by Zach Briggs, Senior Developer and JavaScript Practice Lead with UX design and custom software company Table XI


I’ve been building JavaScript-heavy single-page apps since 2012. So please appreciate how much it pains me to admit that a JavaScript-first approach to development often fails the business, and sometimes fails the users too.

A traditional web app without essential JavaScript is less expensive than an identical single-page app in every way I know how to measure cost. It can be built faster, with fewer developers, saving everybody money.

And yet, single-page apps remain popular. People like the endless whiz-bang interactions — as opposed to the two available with traditional web apps: links and forms. For some functions, JavaScript really is necessary. At Table XI, I’ve struggled to advise clients on how to balance getting the features they want while avoiding the many downsides of loading an application with JavaScript.

The answer is to take the best of both worlds.

It’s possible to ditch 80 to 90 percent of your JavaScript while keeping all the functionality you actually need. We’ll get into the “how” in a bit, but first, why should you cut back as much Javascript?

Users expect traditional link functionality — not JavaScript wizardry

Despite all their flexibility, single-page apps typically use a navigation metaphor that mimics traditional HTML links. With JavaScript routers, you can tap a link to navigate to a new section, use the back button and get URLs that send you back to that exact point. Unless you have a very good reason to veer from this expected behavior, that link metaphor should be the primary means of navigating your app.

Re-creating links and routing with JavaScript is an awful lot of work, but it beats unfamiliar navigation schemes that prevent users from using the back button or sharing links. It’s also necessary if you want search engines to be able to read your site at all, kind of a critical first step for SEO. In contrast, traditional HTML does not require a JavaScript router to be downloaded, parsed and executed by users’ machines.

Traditional web apps are forced to use links for navigation because they have no other options. Single-page apps are forced to use links for navigation because the other options are worse.

Less JavaScript means less code bloat (and better performance)

When we talk about JavaScript, we need to talk about bloat. It’s a problem for both traditional web apps and single-page apps. Traditional web apps always end up needing some form of interaction that cannot be satisfied by links and forms alone. This leads developers to add a new single purpose widget for each novel interaction. It doesn’t take many widgets to make your app noticeably slower. Single-page apps have bloat of a different quality. Bundling an app’s JavaScript into a single file to reduce the number of requests is a common practice. Now think about what happens when you add an About section to a single-page app. The new code drags down the homepage load time, because it’s all bundled together.

Code splitting can help, but you need to be able to use the NPM ecosystem to bundle and package your JavaScript, and you have to have the time budget to configure that system. You’ll need to set up the dependency resolution and module loading portions of your build, which shouldn’t be confused with your ES2015 compiler, which will also need a set of ES6 polyfills. You get the picture. It takes a lot of additional JavaScript to solve single-page-app-bloat.

Search crawlers won’t wait for your JavaScript to load, which hurts your SEO

Google’s web crawlers may be executing JavaScript, but they’re not waiting for async data requests to complete. If you want your site to show up in search, you need to either to pre-render and cache each page or practice the very difficult art of server side rendering (SSR).

The server side rendering of frontend JavaScript solves SEO and load-time problems when executed properly. Unfortunately it often isn’t. SSR is unreliable and expensive, just another example of attempting to solve the problem of using too much JavaScript by writing more JavaScript.

Yet, we still need JavaScript

Even with its costs, there are still good reasons to use Javascript:

  • Traditional web apps limit you to two types of interactions: links and forms.
  • Relying on widgets for all your functionality hamstrings designers and hurts performance.
  • jQuery and its native equivalents involve a lot of typing for not much gain.
  • The old way of blending custom JavaScript with traditional web apps usually results in jQuery spaghetti that’s unmaintainable.

There is an alternative to this all-or-nothing thinking: islands of interactivity.

Add the interactivity, nothing more

Islands of interactivity are like JavaScript widgets that you build yourself with a powerful rendering library. React, Riot, and Vue are three libraries that allow developers to build any type of interaction imaginable with significantly less code.

Not too bad for a library that’s smaller than jQuery.

Unlike a single-page app, Google’s bots can read your JavaScript pieces if you render the JSON data inside of your HTML document, giving the crawler enough time to parse your content. And you don’t need to worry about SSR, because your site is already serving HTML by default. Instead of performing the magic trick of pretending to have a traditional web app, you can just have a traditional web app, with the JavaScript features you need.

How to keep your JavaScript as cost-effective as possible

Islands of interactivity still cost more to develop than a traditional web app, but if you follow this process, you can keep costs down:

  1. Identify the most critical user flows. These may be funnel-type flows, like an e-commerce search, or productivity flows, like a call center dashboard.
  2. Work with design to determine if those user flows can be significantly improved with JavaScript functionality. If they can’t, stop right here.
  3. If the user flow can be improved with JavaScript, and that user flow is critical, then begin that island of interactivity with a static html rough draft.
  4. Replace pieces of the flow with islands of interactivity. Check each iteration with design and product to ensure it’s the right direction.
  5. Repeat for the next most important flow, and the next, until you run out of critical and improvable flows.

The key cost-saving technique is to build your app as a traditional web app until you’ve proven a need for JavaScript. A little goes a long way.

If you’d like to see an example, check out thespicehouse.com using Chrome and the Vue dev tools extension. The site-wide search is using the exact same code as the recipe search, and it’s very little code at that. In contrast, the checkout flow is a single-page app unto itself with many times the complexity. I’m proud of our work on that site and the near-unbelievable ecommerce conversion optimization it produced, but I think I could have used a little less JavaScript in that checkout flow.

Making JavaScript work for your business

Traditional web apps really can’t meet users’ expectations for a modern and engaging website. Since links and forms aren’t enough, there’s a belief that a JavaScript-first approach is the future, inevitable even.

For those of us with budgets and deadlines, however, that JavaScript-first approach may drag our projects under while we write all that extra JavaScript that allows our JavaScript to run quickly and be SEO-compatible.

Developers are paid to solve problems. Making JavaScript work just because it’s there is a self-inflicted problem. Users don’t know whether it’s a single-page app or a traditional web app; they just want it to work well and look appealing.

So give them what they want. “Javascript first” may feel like the future, but building islands of interactivity inside traditional web apps feels like now. Read our 45 best javascript tips and tricks tutorial for more knowledge.

Previous

Microservices Series #4: Beware the monolith

Microservices Series #5: Beware best practices

Next