I am creating a Website that uses Next.js as my framework and Sanity Studio using the SanityPress template. I am trying to create a module that plays a background video from a Video URL that is received by Sanity Studio. When I pass the video url from a Sanity Studio Schema to my Next.js website it causes an error in Sanity Studio.
I tried sanitizing the URL from the module and that works and occupies the video but error the same!
Sanity Studio Error
Schema Module:
<code>import { defineField, defineType } from 'sanity'
import { TfiLayoutCtaCenter } from 'react-icons/tfi'
import { getBlockText } from '../../src/utils'
export default defineType({
name: 'hero.video',
title: 'Hero (Video)',
icon: TfiLayoutCtaCenter,
type: 'object',
fields: [
// other fields
}),
defineField({
name: 'bgVideo', // New field for video background
type: 'url',
title: 'Background Video URL',
description: 'URL of the background video',
options: {
hotspot: true,
},
}),
],
preview: {
select: {
content: 'content',
media: 'bgVideo',
},
prepare: ({ content, media }) => ({
title: getBlockText(content),
subtitle: 'Hero (Video)',
media,
}),
},
})
</code>
<code>import { defineField, defineType } from 'sanity'
import { TfiLayoutCtaCenter } from 'react-icons/tfi'
import { getBlockText } from '../../src/utils'
export default defineType({
name: 'hero.video',
title: 'Hero (Video)',
icon: TfiLayoutCtaCenter,
type: 'object',
fields: [
// other fields
}),
defineField({
name: 'bgVideo', // New field for video background
type: 'url',
title: 'Background Video URL',
description: 'URL of the background video',
options: {
hotspot: true,
},
}),
],
preview: {
select: {
content: 'content',
media: 'bgVideo',
},
prepare: ({ content, media }) => ({
title: getBlockText(content),
subtitle: 'Hero (Video)',
media,
}),
},
})
</code>
import { defineField, defineType } from 'sanity'
import { TfiLayoutCtaCenter } from 'react-icons/tfi'
import { getBlockText } from '../../src/utils'
export default defineType({
name: 'hero.video',
title: 'Hero (Video)',
icon: TfiLayoutCtaCenter,
type: 'object',
fields: [
// other fields
}),
defineField({
name: 'bgVideo', // New field for video background
type: 'url',
title: 'Background Video URL',
description: 'URL of the background video',
options: {
hotspot: true,
},
}),
],
preview: {
select: {
content: 'content',
media: 'bgVideo',
},
prepare: ({ content, media }) => ({
title: getBlockText(content),
subtitle: 'Hero (Video)',
media,
}),
},
})
Next.js Module:
<code>// other imports
import { stegaClean } from '@sanity/client/stega'
import Video from 'next-video' // Import the Video component
export default function Hero({
pretitle,
content,
ctas,
bgImage,
bgImageMobile,
bgVideo,
textAlign = 'center',
alignItems,
}: Partial<{
pretitle: string
content: any
ctas: Sanity.CTA[]
bgImage: Sanity.Image
bgImageMobile: Sanity.Image
bgVideo: string
textAlign: React.CSSProperties['textAlign']
alignItems: React.CSSProperties['alignItems']
}>) {
const hasImage = !!bgImage?.asset
const sanitizedUrl = bgVideo ? encodeURI(bgVideo) : '' // Ensure URL is sanitized
const hasVideo = !!sanitizedUrl // Ensure this is correctly set
console.log('Video URL:', sanitizedUrl)
console.log('Has Video:', hasVideo)
return (
<section
className={cn(
(hasImage || hasVideo) &&
'grid overflow-hidden bg-ink text-canvas *:col-span-full *:row-span-full',
)}
>
{/* Background Video */}
{hasVideo && (
<Video
src={sanitizedUrl} // Provide the sanitized video source URL
autoPlay
loop
muted
playsInline
className="absolute inset-0 h-full w-full object-cover"
onError={(e) => console.error('Video error:', e)}
/>
)}
{/* Background Image */}
{bgImage?.asset && !hasVideo && (
<picture>
<Source image={bgImageMobile} imageWidth={1200} />
<Img
className="size-full max-h-fold object-cover"
image={bgImage}
imageWidth={1800}
draggable={false}
/>
</picture>
)}
{content && (
<div className="section relative z-10 flex w-full flex-col">
<div
className={cn(
'richtext relative isolate max-w-xl [&_:is(h1,h2)]:text-balance',
(hasImage || hasVideo) && 'text-shadow',
hasImage && css.txt,
{
'mb-8': stegaClean(alignItems) === 'start',
'my-auto': stegaClean(alignItems) === 'center',
'mt-auto': stegaClean(alignItems) === 'end',
},
{
'mr-auto': stegaClean(textAlign) === 'left',
'mx-auto': stegaClean(textAlign) === 'center',
'ml-auto': stegaClean(textAlign) === 'right',
},
)}
style={{ textAlign: stegaClean(textAlign) }}
>
<Pretitle
className={cn((hasImage || hasVideo) && 'text-canvas/70')}
>
{pretitle}
</Pretitle>
<PortableText value={content} />
<CTAList
ctas={ctas}
className={cn('!mt-4', {
'justify-start': stegaClean(textAlign) === 'left',
'justify-center': stegaClean(textAlign) === 'center',
'justify-end': stegaClean(textAlign) === 'right',
})}
/>
</div>
</div>
)}
</section>
)
}
</code>
<code>// other imports
import { stegaClean } from '@sanity/client/stega'
import Video from 'next-video' // Import the Video component
export default function Hero({
pretitle,
content,
ctas,
bgImage,
bgImageMobile,
bgVideo,
textAlign = 'center',
alignItems,
}: Partial<{
pretitle: string
content: any
ctas: Sanity.CTA[]
bgImage: Sanity.Image
bgImageMobile: Sanity.Image
bgVideo: string
textAlign: React.CSSProperties['textAlign']
alignItems: React.CSSProperties['alignItems']
}>) {
const hasImage = !!bgImage?.asset
const sanitizedUrl = bgVideo ? encodeURI(bgVideo) : '' // Ensure URL is sanitized
const hasVideo = !!sanitizedUrl // Ensure this is correctly set
console.log('Video URL:', sanitizedUrl)
console.log('Has Video:', hasVideo)
return (
<section
className={cn(
(hasImage || hasVideo) &&
'grid overflow-hidden bg-ink text-canvas *:col-span-full *:row-span-full',
)}
>
{/* Background Video */}
{hasVideo && (
<Video
src={sanitizedUrl} // Provide the sanitized video source URL
autoPlay
loop
muted
playsInline
className="absolute inset-0 h-full w-full object-cover"
onError={(e) => console.error('Video error:', e)}
/>
)}
{/* Background Image */}
{bgImage?.asset && !hasVideo && (
<picture>
<Source image={bgImageMobile} imageWidth={1200} />
<Img
className="size-full max-h-fold object-cover"
image={bgImage}
imageWidth={1800}
draggable={false}
/>
</picture>
)}
{content && (
<div className="section relative z-10 flex w-full flex-col">
<div
className={cn(
'richtext relative isolate max-w-xl [&_:is(h1,h2)]:text-balance',
(hasImage || hasVideo) && 'text-shadow',
hasImage && css.txt,
{
'mb-8': stegaClean(alignItems) === 'start',
'my-auto': stegaClean(alignItems) === 'center',
'mt-auto': stegaClean(alignItems) === 'end',
},
{
'mr-auto': stegaClean(textAlign) === 'left',
'mx-auto': stegaClean(textAlign) === 'center',
'ml-auto': stegaClean(textAlign) === 'right',
},
)}
style={{ textAlign: stegaClean(textAlign) }}
>
<Pretitle
className={cn((hasImage || hasVideo) && 'text-canvas/70')}
>
{pretitle}
</Pretitle>
<PortableText value={content} />
<CTAList
ctas={ctas}
className={cn('!mt-4', {
'justify-start': stegaClean(textAlign) === 'left',
'justify-center': stegaClean(textAlign) === 'center',
'justify-end': stegaClean(textAlign) === 'right',
})}
/>
</div>
</div>
)}
</section>
)
}
</code>
// other imports
import { stegaClean } from '@sanity/client/stega'
import Video from 'next-video' // Import the Video component
export default function Hero({
pretitle,
content,
ctas,
bgImage,
bgImageMobile,
bgVideo,
textAlign = 'center',
alignItems,
}: Partial<{
pretitle: string
content: any
ctas: Sanity.CTA[]
bgImage: Sanity.Image
bgImageMobile: Sanity.Image
bgVideo: string
textAlign: React.CSSProperties['textAlign']
alignItems: React.CSSProperties['alignItems']
}>) {
const hasImage = !!bgImage?.asset
const sanitizedUrl = bgVideo ? encodeURI(bgVideo) : '' // Ensure URL is sanitized
const hasVideo = !!sanitizedUrl // Ensure this is correctly set
console.log('Video URL:', sanitizedUrl)
console.log('Has Video:', hasVideo)
return (
<section
className={cn(
(hasImage || hasVideo) &&
'grid overflow-hidden bg-ink text-canvas *:col-span-full *:row-span-full',
)}
>
{/* Background Video */}
{hasVideo && (
<Video
src={sanitizedUrl} // Provide the sanitized video source URL
autoPlay
loop
muted
playsInline
className="absolute inset-0 h-full w-full object-cover"
onError={(e) => console.error('Video error:', e)}
/>
)}
{/* Background Image */}
{bgImage?.asset && !hasVideo && (
<picture>
<Source image={bgImageMobile} imageWidth={1200} />
<Img
className="size-full max-h-fold object-cover"
image={bgImage}
imageWidth={1800}
draggable={false}
/>
</picture>
)}
{content && (
<div className="section relative z-10 flex w-full flex-col">
<div
className={cn(
'richtext relative isolate max-w-xl [&_:is(h1,h2)]:text-balance',
(hasImage || hasVideo) && 'text-shadow',
hasImage && css.txt,
{
'mb-8': stegaClean(alignItems) === 'start',
'my-auto': stegaClean(alignItems) === 'center',
'mt-auto': stegaClean(alignItems) === 'end',
},
{
'mr-auto': stegaClean(textAlign) === 'left',
'mx-auto': stegaClean(textAlign) === 'center',
'ml-auto': stegaClean(textAlign) === 'right',
},
)}
style={{ textAlign: stegaClean(textAlign) }}
>
<Pretitle
className={cn((hasImage || hasVideo) && 'text-canvas/70')}
>
{pretitle}
</Pretitle>
<PortableText value={content} />
<CTAList
ctas={ctas}
className={cn('!mt-4', {
'justify-start': stegaClean(textAlign) === 'left',
'justify-center': stegaClean(textAlign) === 'center',
'justify-end': stegaClean(textAlign) === 'right',
})}
/>
</div>
</div>
)}
</section>
)
}
1