Adding a Vimeo schema to the Sanity blog template for Sveltekit integration

3 replies
Last updated: Sep 30, 2022
Good morning. I have added a couple additional types to the
bodyPortableText
schema that comes with the Sanity blog template. I’ve got it working in Studio, but I can’t figure out how to render it in Sveltekit. Here’s the schema in studio:
// studio/schemas/bodyPortableText.js

export default {
  name: 'bodyPortableText',
  type: 'array',
  title: 'Post body',
  of: [
    {
      type: 'block',
      title: 'Block',
      // Styles let you set what your user can mark up blocks with. These
      // corrensponds with HTML tags, but you can set any title or value
      // you want and decide how you want to deal with it where you want to
      // use your content.
      styles: [
        { title: 'Normal', value: 'normal' },
        { title: 'H1', value: 'h1' },
        { title: 'H2', value: 'h2' },
        { title: 'H3', value: 'h3' },
        { title: 'H4', value: 'h4' },
        { title: 'Quote', value: 'blockquote' },
      ],
      lists: [
        { title: 'Bullet', value: 'bullet' },
        { title: 'Number', value: 'number' },
      ],
      // Marks let you mark up inline text in the block editor.
      marks: {
        // Decorators usually describe a single property – e.g. a typographic
        // preference or highlighting by editors.
        decorators: [
          { title: 'Strong', value: 'strong' },
          { title: 'Emphasis', value: 'em' },
        ],
        // Annotations can be any object structure – e.g. a link or a footnote.
        annotations: [
          {
            name: 'link',
            type: 'object',
            title: 'URL',
            fields: [
              {
                title: 'URL',
                name: 'href',
                type: 'url',
                validation: (Rule) =>
                  Rule.uri({
                    scheme: ['http', 'https', 'mailto', 'tel'],
                  }),
              },
            ],
          },
        ],
      },
      of: [{ type: 'authorReference' }],
    },
    // You can add additional types here. Note that you can't use
    // primitive types such as 'string' and 'number' in the same array
    // as a block type.
    {
      type: 'mainImage',
      options: { hotspot: true },
    },
    {
      type: 'vimeo',
    },
  ],
}
Within
+page.svelte
I’ll call the portableText using for example
{@html renderBlocks(event.body)}.
The
renderBlocks
function is with the
_sanity.js
config file in Sveltekit for some reason, and is as follows:
import sanity from '@sanity/client';
import blocksToHTML from '@sanity/block-content-to-html';
import imageUrlBuilder from '@sanity/image-url';

/* … projectId, token, etc. here for sanityConfig */
 
export const client = sanity(sanityConfig);

export const imageUrlFor = (source) =>
  imageUrlBuilder(sanityConfig).image(source);

const h = blocksToHTML.h;
const serializers = {
  types: {
    code: ({ node }) => h('pre', h('code', { lang: node.language }, node.code)),
    mainImage: ({ node }) =>
      h('img', {
        src: imageUrlFor(node.asset).url()
      }),
    authorReference: ({ node }) => h('p', 'Author: ' + node.author.name)
  }
};

export const renderBlocks = (blocks) => {
  return blocksToHTML({
    blocks,
    serializers
  });
};
I figure I need to add a type for “vimeo” in the serializer, but can’t seem to get anything returning then. Thank you for anyone who can point me in the right direction.
🙂
AI Update

You're on the right track! You need to add a vimeo serializer to handle your custom type. The issue is that you need to define the serializer in the same way as the other types in your renderBlocks function.

Based on the code you've shared and following the same pattern as your existing serializers, here's what you need to add:

const serializers = {
  types: {
    code: ({ node }) => h('pre', h('code', { lang: node.language }, node.code)),
    mainImage: ({ node }) =>
      h('img', {
        src: imageUrlFor(node.asset).url()
      }),
    authorReference: ({ node }) => h('p', 'Author: ' + node.author.name),
    vimeo: ({ node }) => {
      // Extract the video ID from the URL
      const videoId = node.url.split('/').pop();
      const embedUrl = `https://player.vimeo.com/video/${videoId}`;
      
      return h('div', { class: 'video-container' },
        h('iframe', {
          src: embedUrl,
          width: '640',
          height: '360',
          frameborder: '0',
          allow: 'autoplay; fullscreen; picture-in-picture',
          allowfullscreen: true
        })
      );
    }
  }
};

Important notes:

  1. Define your vimeo schema type - Make sure you have a proper schema definition for the vimeo type in your Studio. It should look something like this:
// In your Studio schema
{
  name: 'vimeo',
  type: 'object',
  title: 'Vimeo Video',
  fields: [
    {
      name: 'url',
      type: 'url',
      title: 'Vimeo URL'
    }
  ]
}
  1. The h function from @sanity/block-content-to-html creates HTML elements. The syntax is:

    • h('tagName', attributes, children)
    • h('tagName', children)
  2. URL transformation - Vimeo URLs that users paste (like https://vimeo.com/123456) need to be converted to embed URLs (https://player.vimeo.com/video/123456) for the iframe to work properly.

  3. Modern alternative - Note that @sanity/block-content-to-html is an older library. For new projects, Sanity recommends using @portabletext/to-html instead, which has similar functionality but is more actively maintained.

The key is making sure the serializer name (vimeo) matches your schema type name, and that you're accessing the correct properties from the node object (in this case, node.url). If you're still having issues, double-check that your vimeo schema is properly registered in your Studio configuration and that the field structure matches what the serializer expects.

Hey
user S
! What have you tried for your vimeo serializer?
hi
user M
! I didn’t mean to ghost you. Life happened and then I forgot. But I did solve it.
For anyone looking for the same, I made a vimeo schema as follows:

import React from 'react'
import ReactPlayer from 'react-player/vimeo'

const vimeoPreview = ({ value }) => {
  const url = value.url
  const wrapperStyle = {
    position: 'relative',
    paddingTop: '56.25%',
  }
  const playerStyle = {
    position: 'absolute',
    top: '0',
    left: '0',
  }
  return (
    <div style={wrapperStyle}>
      <ReactPlayer url={url} controls="true" width="100%" height="100%" style={playerStyle} />
    </div>
  )
}

export default {
  name: 'vimeo',
  type: 'object',
  title: 'Vimeo Embed',
  fields: [
    {
      name: 'url',
      type: 'url',
      title: 'Vimeo video URL',
    },
  ],
  preview: {
    select: {
      url: 'url',
    },
    component: vimeoPreview,
  },
}
Just happy to hear you got it working! Thanks for sharing your solution!

Sanity – Build the way you think, not the way your CMS thinks

Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.

Was this answer helpful?