The Cost of Javascript Frameworks

Tim Kaldec has measured the JavaScript bytes and the JavaScript CPU time for a bunch of sites.

Right now, if you’re using a framework to build your site, you’re making a trade-off in terms of initial performance—even in the best of scenarios.

Some trade-off may be acceptable in the right situations, but it’s important that we make that exchange consciously.

No surprises there. Perhaps the folks at Jira should take a good look at this 😉

The Cost of Javascript Frameworks →

PHP Performance Tip: Use fully-qualified function calls when working with namespaces

TIL: When working with namespaced files in PHP it’s a huge performance win when using fully-qualified function calls.


If you’re calling is_null in your code, PHP will first check for the function’s existence in the current namespace. If not found there, it will look for the function in the global namespace. This extra check is quite a drag, as detailed in this issue for the SCSS PHP Project.

If you do want to target PHP’s built-in is_null (or any other global function), it’s more performant to refer to it using it’s fully-qualified name, e.g. \is_null

❌ Slow:

✅ Fast:

Alternatively you can also import the function first through a use statement.

✅ Fast:
use function is_null;


In the case of SCSS PHP the change from is_null to \is_null lead to a 28% speed increase!. If you’re looking for more benchmarks, Toon Verwerft has done some benching in the past.

Did this help you out? Like what you see?
Thank me with a coffee.

I don't do this for profit but a small one-time donation would surely put a smile on my face. Thanks!

☕️ Buy me a Coffee (€3)

To stay in the loop you can follow @bramus or follow @bramusblog on Twitter.

Via this tweet that sparked my quest into knowing more about this.

Building with Friction

Tim Kaldec takes a look at our modern workflow – in which lots of tools have removed friction – and makes the case to add some “healthy friction”:

A lot of modern workflow improvements have been around removing friction. We want to make it easier to deploy rapidly. Tools like npm make it very easy to gain access to any and all modules we could think of. Tag management enables folks to very quickly add another third-party service.

All of these things, on the surface, provide some value, but the consequences are tremendous. Because these processes remove friction, they don’t ever really give us a chance to pause and consider what we’re doing.

Re-introducing some healthy friction, some moments of pause, in our processes is critical to ensuring a higher level of quality overall.

Examples would be installers that prevent you from installing large bundles, or build pipelines that mark the build as failed when the total bundle size exceeds a certain goal.

Tim Kaldec →

Telling the story of performance

Interesting approach by Clearleft on how they make performance clear to their clients. Instead of showing waterfall charts to their clients, they show them videos:

Download the video of your client’s site loading. Then repeat the test with the URL of a competitor. Now take those videos and play them side by side.

This is so much more effective than showing a table of numbers! Clients get to really feel the performance difference between their site and their competitors.

Show. Don’t tell.

Telling the story of performance →

Should you self-host Google Fonts?

Google Fonts is great, but it also has a downside: it affects your page’s waterfall (during which some render-blocking may occur, as it involves CSS) as explained by Barry Pollard:

The problem is that your website (say loads the stylesheet from, which returns some CSS made up of font-face declarations.

This means you have to connect to, download the CSS, then connect to to download the actual fonts.

Fonts are often discovered late by the browser when loading a page (as you need to download the CSS to see them) but Google Fonts are discovered extra late, as you need to download the CSS from another domain, then the fonts from a 3rd domain and, as discussed above, making an HTTPS connection takes time.

This post goes really deep into how it all works. Covers things such as rel="preconnect", font-display: swap;, etc.

Should you self-host Google Fonts? →

🔥 Harry Roberts said it before: Self-Host your Static Assets

Building mobile-first web animations in React

Talk by Alex Holachek, as brought forward at React Conf 2019:

As the technology to create Progressive Web Apps continues to mature, React developers have the opportunity to write web apps that in some cases can rival native ones in terms of speed and convenience.

However, one barrier to feature parity is the difficulty of creating a native-like UI transition and interaction experience on the mobile web, especially on lower-end devices.

I’ll be discussing various considerations, tips and techniques to create a web app in React that looks, moves, and feels as close to a native mobile app as possible.

Think of gesture-driven animations like these here:

Slides →
Repo →

Smaller HTML Payloads with Service Workers

Philip Walton on how to progressively enhance your site by leveraging Service Workers to fetch partial HTML content and replace it in the DOM:

On this site, after a user visits once and the service worker is installed, that user will never request a full HTML page again. Instead the service worker will intercept requests for pages and just request the contents of those pages—everything inside the <main> element—and then the service worker will combine that content with the rest of the HTML, which is already in the cache.

By only requesting the contents of a page, the networks payloads become substantially smaller, and the pages can load quite a bit faster. For example, on this site over the past 30 days, page loads from a service worker had a 47.6% smaller network payloads, and a median First Contentful Paint (FCP) that was 52.3% faster than page loads without a service worker (416ms vs. 851ms).

The code is implemented using Workbox:

import {cacheNames} from 'workbox-core';
import {getCacheKeyForURL} from 'workbox-precaching';
import {registerRoute} from 'workbox-routing';
import {CacheFirst, StaleWhileRevalidate} from 'workbox-strategies';
import {strategy as composeStrategies} from 'workbox-streams';

const shellStrategy = new CacheFirst({cacheName: cacheNames.precache});
const contentStrategy = new StaleWhileRevalidate({cacheName: 'content'});

const navigationHandler = composeStrategies([
  () => shellStrategy.handle({
    request: new Request(getCacheKeyForURL('/shell-start.html')),
  ({url}) => contentStrategy.handle({
    request: new Request(url.pathname + 'index.content.html'),
  () => shellStrategy.handle({
    request: new Request(getCacheKeyForURL('/shell-end.html')),

registerRoute(({request}) => request.mode === 'navigate', navigationHandler);

Smaller HTML Payloads with Service Workers →

🔗 Related: Unpoly, which uses HTML attributes to let you control partial rerenders using XHR fetches.

Performance Budgets for those who don’t know where to start

Harry Roberts on how to set a Performance Budgets if you really don’t have a clue where to start:

Time and again I hear clients discussing their performance budgets in terms of goals: “We’re aiming toward a budget of 250KB uncompressed JavaScript; we hope to be interactive in 2.75s”. While it’s absolutely vital that these goals exist and are actively worked toward, this is not part of your budgeting. Your budgeting is actually far, far simpler:

Our budget for [metric] is never-worse-than-it-is-right-now.

Harry suggest to measure in periods of two weeks (or whatever the length of your sprints I guess) and always compare against the previous value. If performance is equal or better: great, you’ve got your next maximum to compare against next time. If performance is worse: you’ve got work (or some serious explaining) to do.

By constantly revisiting and redefining budgets in two-weekly snapshots, we’re able to make slow, steady, and incremental improvements.

Performance Budgets, Pragmatically →