Embed Youtube Videos into Your Markdown Editor

Embed Youtube Videos into Your Markdown Editor

This is the second part of the series How I made a Markdown Editor with the ability to embed YouTube Videos, CodePens, Code SandBoxes, etc. If you haven't read the first part, read it here and come back.

In this article, we will specifically see how to embed youtube videos into the markdown editor. Let's divide the task into subtasks.

  1. We need a new markdown syntax that accepts a URL and an optional title with which we can know that the user wants to embed the youtube video there.
  2. Next, we need to convert the youtube URL that we fetched from markup and convert it into its iframe markup.
  3. Next, we need to convert that markdown into the HTML markup that we want.

Let's start by doing the second task.

All we have is a youtube URL, now we need to convert it into its iframe markup. Let's get the official iframe embed that youtube suggests to us.

<!-- Youtube URL -->
https://www.youtube.com/watch?v=PcSUEo0P0GU

<!-- Corresponsing iframe markup copied from youtube embed of the corresponding video -->
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/PcSUEo0P0GU" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

If you see the iframe markup and URL, it's pretty simple to generate that markup from the URL. The main thing to see is the string PcSUEo0P0GU. That's the video id. We just need that video id from the URL, and we can then easily generate the iframe markup.

We can't expect the user to provide the video id directly, which would require the user to know what the video id is and how to get it from the URL. Instead, we will do some regex and fetch the video id from the URL. We can write our own regex, but I found a regex in StackOverflow which fetches id directly from youtube URL

This is the regex that is suggested. Let's just use this for our task.

function getYoutubeVideoId(url) {
    const regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|&v=)([^#&?]*).*/
    const match = url.match(regExp)
    return match && match[2].length === 11 ? match[2] : null
}

Now, we have a function that returns a youtube video id when we pass the youtube URL as an argument.

Now, let's create iframe marup from this video id.

function getYoutubeIframeMarkup (url) {
    const videoId = getYoutubeVideoId(url)
    if (!videoId) {
      return ''
    }
    return `<iframe src="https://www.youtube-nocookie.com/embed/${videoId}" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>`
}

The second task of converting a youtube URL to iframe markup is done. Now, let's get back to creating a custom markdown syntax with which the user can embed the youtube video.

For this task, I am going to use a markdown-it plugin called markdown-it-container. If you don't know what markdown-it is or what its plugins are, go through the first part of this series. I explained everything there.

Markdown-It-Container gives us an interface to create custom block containers with ease. We need to tell the markdown-it-container three things.

  1. Name of the container
  2. How to detect where this container should start and end - basically the validation and detection of custom markdown syntax for this container.
  3. How to render that custom markdown syntax.

The syntax which I will be using is the following.

:::youtube[YOUTUBE_URL]
OPTIONAL_TITLE_OF_VIDEO
:::

You can even simplify the above markdown, but for this tutorial, we will just stick with that syntax.

Now, the markdown-it-container expects two methods, one to validate the markup syntax and another to render it.

I will show you the methods that I used for both and then explain them later.

validate: function (params) {
    return params.trim().match(/^youtube\s*\[(.*)]$/)
},

render: function (tokens, idx) {
    if (tokens[idx].type === 'container_youtube_open') {
        const matches = tokens[idx].info.trim().match(/^youtube\s*\[(.*)]$/)
        if (matches && matches[1]) {
            return (
                '<div class="text-center video-container">' +
                  getYoutubeIframeMarkup({ url: matches[1].trim() }) +
                  '</div><div class="text-center font-weight-light text-capitalize">'
            )
        }
    } else if (tokens[idx].type === 'container_youtube_close') {
        return '</div>'
    }
},

First, the validate method is just trying to validate the syntax that we defined. If you want to know more of what all strings that regex actually matches, go to regexr.com and then paste /^youtube\s*\[(.*)]$/ into the expression text box. You will see step by step explanation of what each character in the regex means. To summarize, it matched strings that are in the form youtube [some_string_here].

Now, let's look at the render function. Now, markdown-it-container automatically creates something called tokens after validating the syntax.

For example, take the following syntax.

:::youtube[YOUTUBE_URL]
OPTIONAL_TITLE_OF_VIDEO
:::

The above syntax generates the following tokens. I removed everything except the relevant parts

// tokens
[
  {
    "type": "container_youtube_open",
     "info": "youtube[YOUTUBE_URL]",
     ......
     ......
  },
  {
    "type": "paragraph_open",
     ......
     ......
  },
  {
    "type": "inline",
    "content": "OPTIONAL_TITLE_OF_VIDEO",
     ......
     ......
  },
  {
    "type": "paragraph_close",
     ......
     ......
  },
  {
    "type": "container_youtube_close",
     ......
     ......
  }
]

Now, all we want to look at is info of the array element that has type container_youtube_open and container_youtube_close. Whenever we get the youtube_container_open tag, we also get the URL in its info. Now, all we have to do is fetch the YOUTUBE_URL from youtube[YOUTUBE_URL] and then convert that URL into iframe markup and render it. That's exactly what I did.

render: function (tokens, idx) {
    // check if the type is 'container_youtube_open'
    if (tokens[idx].type === 'container_youtube_open') {
        // match the strings that are in the form `youtube[YOUTUBE_URL]`
        const matches = tokens[idx].info.trim().match(/^youtube\s*\[(.*)]$/)

        // If matches are found, and there is also a match for the first group, fetch it. That match is our youtube URL.
        if (matches && matches[1]) {
            // Now generate iframe markup from that youtube url.
            return (
                '<div class="text-center video-container">' +
                  getYoutubeIframeMarkup({ url: matches[1].trim() }) +
                  '</div><div class="text-center font-weight-light text-capitalize">'
            )
        }
    } else if (tokens[idx].type === 'container_youtube_close') {
        // when we get to the 'container_youtube_close', close the div that we opened before.
        return '</div>'
    }
},

That's it. Now markdown-it parser can now convert our custom markdown markup for embedding youtube video to HTML markup that we defined. It also accepts an optional video title.

This might be a little too much for beginners(or not?) especially those who haven't used or worked with regex before. So, if you have any concerns or questions related to this blog post, please ask them in the comments, I will try my best to address those.

This blog post has become too long already. Let's stop it here. In the next part of this series, we will see how to embed CodePens and CodeSandboxes to the markdown. It should be straightforward now if you understand everything that I explained in this article. And also let's create an actual editor to visualize these.

I haven't added an actual editor yet, but if you are curious and want to see and play around with the editor which has this youtube embed, you can take a look at markdown-previewer that I created a while ago.

All the code that is written in this article is available on my GitHub at pbteja1998/markdown-editor

If you have any feedback or suggestions to improve the blog post, please leave them in the comments. I will try to improve the post accordingly.

If this blog post was helpful to you, please consider liking it and sharing it so that it reaches more people.

If you want to get notified as soon as I write a new blog post, you can click on the Subscribe button at the top of this page. You can also follow me here at Bhanu Teja P or on twitter at @pbteja1998 to get updated.

Links and References

Did you find this article valuable?

Support Bhanu Teja Pachipulusu by becoming a sponsor. Any amount is appreciated!

ย