Leveling Up my Sveltekit / Sanity.io Blog Content with Featured Videos and Syntax Highlighting

Published: 2/17/2022

Main blog

It's been 7 weeks since I rebuilt my personal site with a blog element, and I have loved writing articles on a whole slew of topics relating to Web Development and the Software Industry. The support that I've received and has been phenomenal and I am having a really good time with this project. That being said, I knew that there was already room for improvement.

My blog content was missing a few things, so I decided that I wanted to get some items on my task board. The two major items on it were embedded YouTube videos, and syntax highlighted code blocks. They both took a little bit of coding and configuration, so I figured that I would write up a quick little tutorial on how I added these two enhancements to my blog posts. If you read my write up about building a blog with Sveltekit and Sanity.io, and used it as a guide for your own blog, this post should give you some easy ways to level up your content.

Embedding YouTube Videos

I recently started a YouTube Channel to add some supplemental content to this blog, and it's been going really well. However, I was just putting large images that linked to the YouTube video in my blog posts. I wanted to be able to embed the YouTube videos themselves in my posts so that you don't have to click away from the post if you don't want to.

So what was I going to have to do? Well, my first thought was to just embed the YouTube videos in the posts themselves. The problem is that all my posts are markdown that is stored in Sanity.io. No problem, I can add a featured video to my Sanity.io posts schema so that I have something similar to the featured image. There should be a plugin for that, right?

I took a look around at the Sanity.io plugins and didn't find a featured video option. That being said, I did find this great write-up on adding a YouTube video block for block content. I'm not using block content though, so I had to take a slightly different approach. I fired up my sanity.io project on my local machine and started adding some code.

I started by creating a youtube.js file in my schemas directory. I added the following code in the file. This is taken straight from Sanity's writeup, but I want to include my whole process here:

export default {
    name: 'youtube',
    type: 'object',
    title: 'YouTube Embed',
    fields: [
        {
            name: 'url',
            type: 'url',
            title: 'YouTube Video URL'
        }
    ]
}

This code sets up the YouTube object type that has a url field. To include this in my post schema I had to add it to my main schema.js file like this...

import schemaTypes from 'all:part:@sanity/base/schema-type'

import blockContent from './blockContent'
import category from './category'
import post from './post'
import author from './author'
import youtube from './youtube'

export default createSchema({
  name: 'default',
  types: schemaTypes.concat([
    post,
    author,
    category,
    blockContent,
    youtube
  ]),
})

...which is simply adding the YouTube object to the types in our schema. Then I modify my post schema so that I can have the YouTube URL field in our posts in the Sanity Studio:

export default {
  name: 'post',
  title: 'Post',
  type: 'document',
  fields: [
    {
      name: 'title',
      title: 'Title',
      type: 'string',
    },
    {
      name: 'slug',
      title: 'Slug',
      type: 'slug',
      options: {
        source: 'title',
        maxLength: 96,
      },
    },
    {
      name: 'excerpt',
      title: 'Excerpt',
      type: 'string'
    },
    {
      name: 'author',
      title: 'Author',
      type: 'reference',
      to: {type: 'author'},
    },
    {
      name: 'mainImage',
      title: 'Main image',
      type: 'image',
      options: {
        hotspot: true,
      },
    },
    {
      name: 'YouTube',
      title: 'YouTube',
      type: 'youtube'
    },
    {
      name: 'categories',
      title: 'Categories',
      type: 'array',
      of: [{type: 'reference', to: {type: 'category'}}],
    },
    {
      name: 'publishedAt',
      title: 'Published at',
      type: 'datetime',
    },
    {
      name: 'body',
      title: 'Body',
      description: 'What the reader will see.',
      type: 'markdown',
    },
  ],

  preview: {
    select: {
      title: 'title',
      author: 'author.name',
      media: 'mainImage',
    },
    prepare(selection) {
      const {author} = selection
      return Object.assign({}, selection, {
        subtitle: author && `by ${author}`,
      })
    },
  },
}

As you can see, we've added YouTube to the list of fields on a post, and if I rebuild my Sanity Studio and then launch it, I see the following on my posts:

image

Great! Adding the posts in Sanity is only half the battle though. I need to make sure I'm displaying the videos on the frontend. I opened up my website project and jumped in to the [slug].svelte file. First, I needed to change my query for the data from Sanity. Here is what it was before:

        const query = `*[ slug.current == '${ url.pathname.split('/').slice(-1)[0] }']{title, excerpt, publishedAt, slug, body, "categories": categories[]->title, "imageUrl": mainImage.asset->url }`

And here it is now:

        const query = `*[ slug.current == '${ url.pathname.split('/').slice(-1)[0] }']{title, excerpt, publishedAt, slug, body, "categories": categories[]->title, "imageUrl": mainImage.asset->url, YouTube }`

This displays something I love about working with Sanity.io. GROQ is such a powerful query language, and it's syntax is so simple. If I need YouTube is my query results, I just add YouTube.

To display the YouTube video, I added the following after my post body:

    {#if post.YouTube}
    <iframe width="560" height="315" src={post.YouTube.url} title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
    {/if}

In this block, I use the Svelte if block to check if there is a featured video. If so, I simply include the embed iframe from YouTube, with the src bound to the url we pull from sanity. Easy PZ. You can see an example of this at the end of this post, where I've featured my most recent YouTube video as of the time of this writing.

Finally, there are some drawbacks to this method. I can only include one video per post, which is why I used the term "featured video". Furthermore, I have to modify the URL of the video in Sanity to be the embed link instead of the default watch link so it displays the video correctly. I can live with these trade offs, and I'm happy to include videos in my posts.

Syntax Highlighting

What is the point of sharing my code if you can't read it? Before I added syntax highlighting, my code was unformatted, and it was white on a dark grey background. It hurt my eyes to look at, and I know it wasn't helping my readers. I have been wanting to add Syntax highlighting for a while, but I didn't know how to make it work in my markdown posts. Then I had the realization that I was using Svelte-Markdown which was converted my markdown in to markup, and I could modify my code blocks easily. Let me show you how.

First things first, I downloaded a local copy of Svelte-Markdown from github. I was already making some styling modifications to it, and applying them with patch-package as a post install script. However, this would sometimes break my Svelte build process, and I was starting to make too many changes to keep Svelte-Markdown as a node module. I dropped the files in to a utils folder in the src directory of my project and made the pre-existing modifications (making images mobile responsive and the previous code block styling). Then I set to work changing the code blocks.

I opened up the code.svelte file in /src/utils/svelte-markdown-main/renderers, and tried to simply add in the highlight.js library. That didn't work, and it wouldn't have accurate svelte syntax highlighting. I moved on and tried the Svelte-Prism node-module. I think that this would have worked well, and I may try it in the future, but the docs didn't include information on making it work with Sveltekit, and I gave up. Finally, I found Svelte-Highlight. It had everything I needed. Svelte syntax highlighting, highlight themes, and instructions on making it work with Sveltekit (which I could have used with Svelte-Prism in hindsight). I added the highlight.js library as an optimized dependency in my vite configuration block and made the following changes to code.svelte:

<script>
  import { HighlightSvelte } from "svelte-highlight";
  import vs2015 from "svelte-highlight/src/styles/vs2015";

  export let text
  $: code = text
</script>

<svelte:head>
  {@html vs2015}
</svelte:head>

<HighlightSvelte {code} />

Let's break this down. First, we import HighlightSvelte. Then, we import our theme. I stuck with VS2015, because I think most people have seen it at least once before. I use Cobalt in VS Code, but I couldn't find a close match in the highlight themes. I then assign the value of the text prop to the a reactive statement. This was my first time reaching for a reactive statement in Svelte and it blew my mind. I'm going to do a whole post on these, but just know that you need it for this method to work. I use svelte:head to include my theme files, and then instatiate the HighlightSvelte component with the code from our reactive statement. I rebuilt the project and just like that, syntax highlighting! I would show you an example, but you've been looking at them in this whole post!

I couldn't be more pleased with how much better this made my posts and how much easier it made them on the eyes. I may play around with more themes in the future, but for now, I like what I have!

Wrapping up

Just like that, both of my blog enhancements were knocked out. I don't have to do any extra work other than adding a YouTube url to my posts in Sanity studio, and everything is looking good! If you liked this post and want to see more about blog building with Sveltekit and Sanity.io, let me know if our discord. If you made it this far, I appreciate you reading! Until next time, cheers!

Ryan

Thank you so much for reading this post and for sticking around! I want every article I put out to bring value to those in my community and the dev community as a whole. If you are new here, thanks for visiting. If you are returning, thank you for your continued support. Remember that if you haven't yet, you can join our growing community on our discord server. Cheers!