I’m working on a Next.js project where I need to render a carousel that can display either images or videos based on the media type. The media data comes from Sanity.io, and I’m having some trouble getting it to work properly.
My current approach is only populating the captions in the carousel, but no image or video.
Here’s a snippet from my Sanity schema:
`{
"name": "carousel",
"title": "Carousel",
"type": "array",
"of": [
{
"type": "image",
"fields": [
{
"name": "caption",
"type": "array",
"of": [{ "type": "block" }],
"title": "Caption"
}
]
},
{
"type": "file",
"fields": [
{
"name": "caption",
"type": "array",
"of": [{ "type": "block" }],
"title": "Caption"
},
{
"name": "type",
"title": "Type",
"type": "string",
"options": {
"list": [
{ "title": "MP4", "value": "video/mp4" }
]
}
}
],
"title": "Video"
}
]
}
`
And here’s my Page component:
import { getProject } from "@/sanity/sanity-utils";
import { PortableText } from "@portabletext/react";
import { ProjectCarousel } from "@/app/components/projectCarousel";
import Image from "next/image";
import { Key } from "react";
type Props = {
params: { project: string };
};
export default async function Project({ params }: Props) {
const slug = params.project;
const project = await getProject(slug);
return (
<div>
<h1>Project</h1>
<header>
<h1>{project.title}</h1>
</header>
<div>
<PortableText value={project.description} />
</div>
<div>
<ProjectCarousel>
{Array.isArray(project.carousel) &&
project.carousel.map(
(
mediaItem: {
image?: string;
alt?: any;
caption?: any[];
type?: string;
video?: string;
},
index: Key | null | undefined
) => (
<div key={index}>
{mediaItem.image ? (
<Image
src={mediaItem.image}
alt={mediaItem.alt || "Carousel image"}
width={800}
height={600}
/>
) : (
mediaItem.video && (
<video controls width="800" height="600">
<source src={mediaItem.video} type="video/mp4" />
</video>
)
)}
{mediaItem.caption && (
<div>
{mediaItem.caption.map((block, i) => (
<p key={i}>{block.children[0].text}</p>
))}
</div>
)}
</div>
)
)}
</ProjectCarousel>
</div>
</div>
);
}