Look. There’s no getting around it. Weighing in at 626KB (170KB gzipped), Mux Player is a bit of a chonk.
So what do we do about that? And can we fix that while looking stylin'?
So what about the code? You might be familiar with normal import statements. They look something like this:
It’s just a small change to kick @mux/mux-player out into its own bundle:
And here’s the best part of this syntax. We can actually put this statement anywhere! For example, let’s say we only want to load the @mux/mux-player bundle when the player element is on screen. We can put that dynamic import statement inside an Intersection Observer, like this:
Meanwhile, in React, we have access to React.lazy and React.Suspense, which make it easier to do all of this with React components. Importing a component with React.lazy puts off loading that component until it is first rendered. Suspense offers us an option to show a fallback while that component loads. (More on fallbacks later.) So, in React, loading Mux Player once it enters the window looks something like this:
Of course, that’s not the end of the story. We still have another problem. When the player bundle finally loads, Mux Player makes all the content jump around. That’s called having poor cumulative layout shift (CLS), and it once again might make our users sad and our SEO sad. So now what?
When Mux Player finally loads, it all of a sudden takes up a bunch of vertical space, making the whole layout shift. Let’s fix that by saving some space for Mux Player with a placeholder. Then, once the bundle loads, let’s swap that placeholder out for the mux-player element.
First, let’s make a placeholder that is the same size as Mux Player. The CSS aspect-ratio property and our Mux Video asset’s aspect_ratio property make this pretty straightforward. With aspect ratio, as long as we tell our browser how wide an element should be, the browser sets the height automatically regardless of the element’s content. We’ll use aspect ratio to set the size of a wrapper, and then make sure both the placeholder and the player are the exact same size as the wrapper:
Meanwhile, in React, Suspense does the work for us:
Oh my goodness, though — that placeholder is boring. And what’s worse, it’s kinda awkward for the user, isn’t it? Imagine being on a slow connection and just seeing a white rectangle. Why is there this weird gap on the page? What’s going on here?
Fear not, fair reader! Following the example of next/image and many before it, we’re going to compute a blurhash to display while the player loads. A blurhash is a lightweight, multicolor gradient representation of an image. Here’s what we’re cooking today:
Step one: What image can we blurhash that will best represent the incoming player? Well, because we read the docs, we know that Mux Player will use a thumbnail from the middle of the video as a poster. Let’s grab the same thumbnail with image.mux.com to use for our blurhash.
Next step: Making it blurry. To do that, we’ll use a combination of the sharp and blurhash packages. Because these are heavier packages and a more computationally expensive step, we’ll definitely want to run this server-side. (Our site is Next.js, so we’re running this step in getStaticProps or getServerSideProps.)
Well, that’s all fine and dandy, but what can we do with a blurhash? We could use the decode function and pass those pixels to a canvas client-side, but let me do you one better. If we convert our blurhash to a base64 Data URL, we’re easily able to display the blurhash in an image element or as a CSS background image without any client-side code. Something like this:
Final step: Let’s add that blurhash to our front-end! We add it not just to the placeholder, but also to mux-player itself. That way, mux-player will show the placeholder while it loads the poster over the network. Neat!
Not gonna lie; some of that placeholder stuff got kinda dicey at the end. I wouldn’t blame you if you wanted to take it easy and not do this.
Mux Player React Lazy takes whatever’s in the placeholder= attribute of the Mux Player element and displays that as a placeholder. It’ll swap that placeholder out when the player enters the viewport.
Meanwhile, Mux Blurhash bundles all that sharp and blurhash work into one nice function, muxBlurHash(playbackId, options). Used together, you get happy users and happy SEO.
Here’s what it all looks like in Next.js.
Pretty nice, right? You can get started with our docs on lazy-loading Mux Player and providing a placeholder for Mux Player. And if you have any questions, don’t be afraid to hit us up at @MuxHQ on Twitter.