Bed for Craft CMS

Free · Craft CMS 5

The page moved as you read

An embed arrives in a page as somebody else's markup and behaves like it. It loads whatever it wants whenever it wants, and it settles to a height nobody knew in advance — after the reader has already started reading. Bed lays a bed for each one: space reserved from what the embed actually measured on real page views, loading deferred until it is wanted, and connections warmed only for the ones that are wanted immediately.

Bed

It happens on the way out the door

No field, no shortcode, no template change. Bed rewrites the finished response, so it does not care whether the embed came from a rich-text field, a hard-coded template or another plugin's render.

html
<!-- what you pasted -->
<iframe src="https://player.vimeo.com/video/76979871"
        width="640" height="360"></iframe>

<!-- what goes out -->
<div class="bed bed--vimeo bed--iframe bed--ratio"
     data-bed="e87d290a…" style="--bed-ar:640/360">
  <iframe src="https://player.vimeo.com/video/76979871"
          width="640" height="360" loading="lazy"
          title="Vimeo embed"
          referrerpolicy="strict-origin-when-cross-origin"></iframe>
</div>

<!-- and for the ones whose height nobody can know in advance, later: -->
<style>
  [data-bed="2a8cbce…"]{--bed-min:550px}
  @media (min-width:1025px) and (max-width:1280px){
    [data-bed="2a8cbce…"]{--bed-min:623px}
  }
</style>

Features

Four things that make an embed behave, and two that make the first four honest.

Measured, not guessed

A guess that is too big leaves a hole and a guess that is too small still moves the page. So a sampled fraction of visitors beacon back what each embed settled to at their own viewport width, and every later render reserves that.

  • Seven viewport buckets, emitted as a media-query ladder
  • Widths nobody has visited stay unreserved rather than guessed

Known shapes need no measurement

A YouTube frame, or anything carrying width and height, gets an aspect-ratio box on its very first render. Recognition buys the natural ratio for free — there is nothing to wait for.

  • Around sixty providers, each with its own natural ratio
  • An unrecognised embed still gets a bed from its own dimensions

Lazy, except at the top

Frames get loading="lazy" and media gets preload="none" — but the first embed on the page, and anything measured inside the first screenful, is left eager.

  • Above the fold at any width counts as above the fold everywhere
  • Lazy-loading the LCP element is the one mistake that actually costs

Loader scripts lifted out

A tweet, a TikTok, an Instagram post — the provider's loader script is lifted out of the document entirely and injected by an IntersectionObserver when the embed nears the viewport.

  • Root margin is a setting, so it starts before the reader arrives
  • Bed checks the runtime is really on the page before it lifts anything

A facade in front of the player

A video player is a whole document, a few hundred kilobytes of script and a handful of third-party connections, all spent before anybody has decided to watch anything. Bed puts a poster and a play button there instead.

  • The real player swaps in on the click, with autoplay
  • Turn posters off and a reader who never clicks makes no third-party request at all

Warm connections, on a budget

Preconnect for the providers whose embeds load immediately, dns-prefetch for the rest — deduped and capped, because a preconnect is a socket, a DNS lookup and a TLS handshake opened on spec.

  • Six per page by default: a budget, and never a target
  • A provider measured above the fold is upgraded to preconnect automatically

Frequently Asked Questions

The questions worth answering before you install it.

Free, and it stays free

No editions, no licence key, no trial. Install it, and the next page you render has a bed under every embed on it.