Fast / Private MDX YouTube Embed

The more I’ve played around with Gatsby and Netlify the more I’ve become focused on making sure this site feels really fast when you visit it. This is my personal stand against how unpleasant it is to visit websites these days.

To that end, I made myself a little React component to embed YouTube videos. Here are some features:

  • Page HTML just has the static frame image in an html›<a> element
  • Clicking on the html›<a> replaces it with the YouTube html›<iframe>
  • MDX-friendly, fully compatible with SSR
  • Uses the ytimg.com and youtube-nocookie.com domains so you don’t get tracked
  • Mild support for Styled System / Theme UI
  • Adds &rel=0 to the video embeds so you don’t accidentally send your visitors on YouTube’s path to right-wing radicalization
  • Responsive sizing

Here’s what it looks like, featuring a video of my Magic: the Gathering Life Tracker:

<YouTube id="sDQq5ULZEfg" title="MtG Gears Life Counter" />

I haven’t packaged this up yet as like a thing, but the source code’s pretty small if you want to copy / customize it: YouTube.tsx

Implementation

Update, 9/15/19: Changed the HTML to generate an html›<a> instead of a html›<button> and move more styles from Emotion into html›style attributes for better RSS reader support.

The component generates static HTML that looks like this:

<div
  class="gatsby-resp-iframe-wrapper"
  style="
    padding-bottom:56.25%;
    height:0;
    position:relative;
    overflow:hidden;
  "
>
  <a
    href="https://www.youtube.com/watch?v=sDQq5ULZEfg"
    style="
      background-image:
        linear-gradient(to bottom, #BC007B, rgba(255, 255, 255, 0) 45%),
        url(https://i.ytimg.com/vi/sDQq5ULZEfg/0.jpg);
      background-size:cover;
      background-position:center;
      border:none;
      width:100%;
      height:100%;
      position:absolute;
      display:block;
    "
  >
    <svg
      aria-labelledby="youtube-player-play-button-title"
      style="
        position:absolute;
        width:4em;
        top:50%;
        left:50%;
        margin-top:-2em;
        margin-left:-2em;
        transform:rotate(90deg);
      "
    >
      <title id="youtube-player-play-button-title">Click to play video</title>
      <g
        transform="translate(0.000000,1126.000000) scale(0.100000,-0.100000)"
        fill="#ffffff"
        stroke="none"
      >
        <path d="…"></path>
      </g>
    </svg>
  </a>

  <a
    href="https://www.youtube.com/watch?v=sDQq5ULZEfg"
    style="
      position:relative;
      display:inline-block;
      color:white;
      text-shadow:0 0 5px rgba(0, 0, 0, 0.4);
      padding:1em;
    "
  >
    MtG Gears Life Counter
  </a>
</div>

When you click the first html›<a> tag, the React component replaces the contents of the html›<div> with a standard YouTube embed html›<iframe>:

<iframe
  src="https://www.youtube-nocookie.com/embed/sDQq5ULZEfg?rel=0&modestbranding=1&autoplay=1"
  allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture"
  allowfullscreen=""
  style="
    position: absolute;
    top: 0px;
    left: 0px;
    width: 100%;
    height: 100%;
  "
  frameborder="0"
>
</iframe>

Since it has html›autoplay=1 set, the video starts playing immediately, so there’s no second click needed (at least on desktop browsers).

Integrating into Gatsby / MDX

The most convenient way to use this component in Gatsby is through an MDX “shortcode.” This is a feature of MDX that lets you auto-import React components into your MDX render context, so you don’t need to use manual javascript›import statements.

Since we want the component to run for RSS feed generation as well as our posts (and other content types) we’ll need to register it as high as possible in our configuration, which means gatsby-ssr and gatsby-browser:

import YouTube from '../src/components/YouTube';

const MDX_COMPONENTS = { YouTube };

export const wrapRootElement = ({ element }) => (
  <MDXProvider components={MDX_COMPONENTS}>{element}</MDXProvider>
);

Note how we didn’t declare the jsx›<MDXProvider components={…}> prop inline, but used a constant instead. See the caveats section of the MDXProvider documentation for an explanation.

Alternatives / Inspirations

  • gatsby-remark-responsive-iframe is key for making the iframe look nice on the page. I don’t use it directly, since the aspect ratio for YouTube embeds is known, but I do keep the gatsby-resp-iframe-wrapper class name in case a site is styling it particularly.
  • gatsby-remark-embed-video has a nice syntax in the Markdown, which I might try to copy later. It just embeds the html›<iframe> directly, though.
  • gatsby-remark-link-youtube was the source for automatically using a thumbnail provided by YouTube rather than an embedded html›<iframe>.

TODOs

  • [ ] Better Theme UI integration so it can more reliably fit into other sites than mine
  • [ ] Fix autoplaying in Safari on iOS
  • [ ] Uniquify the html›id attribute on the html›<title>
  • [ ] Make an MDX or Remark plugin so you can use the `youtube:sDQq5ULZEfg` syntax in your Markdown rather than the JSX component
  • [ ] Automatically get the video’s title from YouTube on page generation so you don’t have to provide it
  • [x] Better rendering in RSS feeds
  • [ ] Browser testing other than Chrome / Firefox on OS X (is that even a thing anymore?)
  • [ ] Package it up in some friendly way?

That’s it!

If you use this on your own site, please let me know! And if you can help me learn how to make the Theme UI support better, I’d really appreciate it. @-mentions or DMs are best: @fionawhim