Easy URL embeds in Markdown

You can handle multiple platforms embeds—like YouTube, X (Twitter), Instagram, Vimeo, and Spotify—with a single client-side JavaScript approach or a Ruby plugin hook.

Because platforms like X and Instagram use full interactive widgets rather than simple <iframe> elements, using JavaScript is usually the most reliable solution.


The Universal JavaScript + CSS Solution

This script checks paragraphs in your content for standalone URLs from supported platforms and swaps them out with their corresponding responsive embed HTML or platform scripts.

1. Add CSS for <iframe> embeds (YouTube, Spotify, Vimeo)

Add this to your CSS file (e.g., assets/css/style.scss):

/* Responsive container for video/audio standard iframes */
.responsive-embed-container {
  position: relative;
  padding-bottom: 56.25%; /* 16:9 Aspect Ratio */
  height: 0;
  overflow: hidden;
  max-width: 100%;
  margin: 1.5rem 0;
}

.responsive-embed-container iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Center social widgets like X & Instagram */
.social-embed-container {
  display: flex;
  justify-content: center;
  margin: 1.5rem 0;
}


2. Add the JavaScript Parser

Add this script before </body> (e.g., in _includes/footer.html or _layouts/post.html).

<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<script async src="https://www.instagram.com/embed.js"></script>

<script>
document.addEventListener("DOMContentLoaded", function() {
  const content = document.querySelector(".post-content") || document.querySelector("main");
  if (!content) return;

  const paragraphs = content.querySelectorAll("p");

  paragraphs.forEach(p => {
    const url = p.innerText.trim();
    
    // Ensure the paragraph contains ONLY a URL
    if (!url.match(/^https?:\/\/[^\s]+$/)) return;

    let embedHTML = "";

    // 1. YouTube
    const ytMatch = url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]{11})/);
    if (ytMatch) {
      embedHTML = `
        <div class="responsive-embed-container">
          <iframe src="https://www.youtube-nocookie.com/embed/${ytMatch[1]}" allowfullscreen></iframe>
        </div>`;
    }

    // 2. X (Twitter)
    const xMatch = url.match(/(?:twitter\.com|x\.com)\/([a-zA-Z0-9_]+)\/status\/([0-9]+)/);
    if (xMatch) {
      embedHTML = `
        <div class="social-embed-container">
          <blockquote class="twitter-tweet"><a href="${url}"></a></blockquote>
        </div>`;
    }

    // 3. Instagram
    const igMatch = url.match(/(?:instagram\.com)\/(?:p|reel)\/([a-zA-Z0-9_-]+)/);
    if (igMatch) {
      embedHTML = `
        <div class="social-embed-container">
          <blockquote class="instagram-media" data-instgrm-permalink="${url}"></blockquote
        </div>`;
    }

    // 4. Vimeo
    const vimeoMatch = url.match(/vimeo\.com\/([0-9]+)/);
    if (vimeoMatch) {
      embedHTML = `
        <div class="responsive-embed-container">
          <iframe src="https://player.vimeo.com/video/${vimeoMatch[1]}" allowfullscreen></iframe>
        </div>`;
    }

    // 5. Spotify (Tracks, Albums, Playlists)
    const spotifyMatch = url.match(/open\.spotify\.com\/(track|album|playlist|episode)\/([a-zA-Z0-9]+)/);
    if (spotifyMatch) {
      embedHTML = `
        <div style="margin: 1.5rem 0;">
          <iframe src="https://open.spotify.com/embed/${spotifyMatch[1]}/${spotifyMatch[2]}" 
                  width="100%" height="152" frameborder="0" allow="encrypted-media"></iframe>
        </div>`;
    }

    // If a match was found, replace the <p> tag
    if (embedHTML) {
      p.outerHTML = embedHTML;
    }
  });

  // Re-trigger social widgets rendering if necessary
  if (window.twttr && window.twttr.widgets) window.twttr.widgets.load();
  if (window.instgrm && window.instgrm.Embeds) window.instgrm.Embeds.process();
});
</script>


How it works in Markdown

Just drop raw URLs on their own lines:

Here is a YouTube video:

<div class="embed embed-video" data-embed="video" style="--embed-ratio: 56.25%;">
  <div class="embed-video__inner">
    <iframe src="https://www.youtube-nocookie.com/embed/dQw4w9WgXcQ" title="YouTube video" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen loading="lazy"></iframe>
  </div>
</div>


Check out this post on X:

<div class="embed embed-twitter" data-embed="twitter">
  <blockquote class="twitter-tweet" data-dnt="true">
    <a href="https://twitter.com/i/status/1683528642000000000">View post on X</a>
  </blockquote>
</div>


Or this Spotify track:

<div class="embed embed-spotify" data-embed="spotify">
  <iframe style="border-radius:12px" src="https://open.spotify.com/embed/track/4cOdK2wGLETKBW3PvgPWqT" width="100%" height="152" frameborder="0" allowfullscreen allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture" loading="lazy"></iframe>
</div>


Comment by email Submit a PR

← Back to blog