Posts tagged pagespeed

November 3, 2021

The Mysterious Case of Emotion and “exports is not defined”

Thought I’d share a bug I ran into a while back that sent me on a Poirot style investigation full of red herrings and unexpected culprits.

This is tangentially related to my recent page speed woes at work. We’d started using Emotion for CSS-in-JS in our component library and, combined with lazy and conditional component loading, it helped with some of the “Reduce unused CSS” warnings we were seeing in Lighthouse.

So adding Emotion as a styling option in our main codebase seemed like an obvious choice. We’d already installed @emotion/core (v10) when we started importing from our component library which meant it should be a quick, two-step process: 1) running yarn add @emotion/babel-preset-css-prop and 2) adding it to our babel.config.js presets after @babel/preset-react. I followed those steps, ran Webpack, and promptly got the error “ReferenceError: exports is not defined”.

Weird.

Continue Reading

July 29, 2021

Preloading Images in a Responsive, WebP World

Watch out for giant images

It’s time for another Google Core Web Vitals themed TIL. This one concerns image loading. Specifically, huge, above the fold image preloading.

The site I work on uses a lot of super wide to full width hero images. We’d done some previous optimizations around serving images in WebP format (Serve images in modern formats) and using picture and srcset for responsive images (Properly size images). I won’t go into details since those topics already have a ton of tutorials that explain things better than I ever could. In fact, Smashing Magazine alone has you covered with Using WebP Image Format Today and Responsive Images Done Right: A Guide To picture And srcset.

Preload critical assets to improve loading speed.

The one downside to our earlier work? It made things a little more confusing when we started looking into preloading images.

Preloading assets that aren’t actually used isn’t particularly helpful (Chrome will even warn you if you do it!). If you’re serving a WebP image but preloading the fallback or preloading a mobile image on a desktop browser, you aren’t getting any benefits out of it.

Let’s say you have a picture block that selects an image based on browser support, screen resolution, and breakpoint:

<picture>
  <source srcset="large-image.webp, large-image-2x.webp 2x" media="(min-width: 768px)" type="image/webp" />
  <source srcset="large-image.jpg, large-image-2x.jpg 2x" media="(min-width: 768px)" />
  <source srcset="small-image.webp, small-image-2x.webp 2x" type="image/webp" />
  <img src="fallback-image.jpg" srcset="small-image.jpg, small-image-2x.jpg 2x" />
</picture>

The first question is how to handle different image formats. Looking at the Can I use entries for Webp image formats and preload, it seems pretty unlikely for a browser to support preloading but not WebP images. And according to a quick aside in this Addy Osmani article on hero images, you can use the type="image/webp" attribute with a link tag to specify that you want to preload a WebP image.

Next, we need to deal with screen sizes and resolutions. In addition to type, the link tag supports media and imagesrcset attributes which should look very similar to the media and srcset attributes on our source and img tags. The main difference is that you have to be more explicit with your breakpoints since the browser won’t be choosing the best image itself.

Putting that all together, you would add this code to your head tag to preload the images from the example above:

<link rel="preload" as="image" href="large-image.webp" media="(min-width: 768px)" imagesrcset="large-image.webp, large-image-2x.webp 2x" type="image/webp" />
<link rel="preload" as="image" href="small-image.webp" media="(max-width: 767px)" imagesrcset="small-image.webp, small-image-2x.webp 2x" type="image/webp" />

If you prefer an example with real images, check out this Pen on Codepen. Just click on Settings and HTML to view the preloading the code.

May 21, 2021

Optimizing Lodash imports with jscodeshift

Avoid enormous network payloads!

Since the beginning of the year, I’ve spent a lot of time at work getting ready for Google Core Web Vitals-ageddon. Most of the techniques we’ve tried are fairly well-documented and I don’t want to retread the great advice and tutorials that are already out there (although I should put together a roundup of links). A few have required a little more investigation and experimentation, though, and those seemed worth writing up.

Remove unused JavaScript! Avoid enormous network payloads!

One easy trick for creating huge JavaScript bundles and making Google angry is importing the entire Lodash library when you only use a few methods. A lot has been written on Lodash and bundle sizes and best practices for imports (I’m partial to The Correct Way to Import Lodash Libraries - A Benchmark on BlazeMeter) but what I found lacking were tips on how to update an older, monolithic Rails app with inconsistent import patterns and the continual risk of unmanageable merge conflicts.

Enter jscodeshift.

jscodeshift is a toolkit that allows you to run codemods over JavaScript files and it was a lifesaver in this situation. According to the article above, direct imports are the way to go and jscodeshift makes it possible to instantly transform any files:

  • Importing the full library (import _ from 'lodash')
  • Importing methods from Lodash with curly brackets (import { name } from 'lodash')
  • Calling methods starting with _.
To get started with jscodeshift, run npm install -g jscodeshift to install it globally and follow along below.

Continue Reading