I have a Shadcn UI carousel that should display a bunch of images; the carousel controls are there and they seem to fit into the screen, but the images are not being displayed.
Note: If I replace the property fill with the width and height I see the image.
This is my component:
"use client";
import { getAllImages } from "@/lib/appwrite";
import { useQuery } from "@tanstack/react-query";
import React, { useEffect, useState } from "react";
import {
Carousel,
CarouselApi,
CarouselContent,
CarouselItem,
CarouselNext,
CarouselPrevious,
} from "@/components/ui/carousel";
import Image from "next/image";
import { Loader2, AlertCircle } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
export default function FullScreenCarousel() {
const [api, setApi] = useState<CarouselApi>();
const [current, setCurrent] = useState(0);
const [count, setCount] = useState(0);
const {
data: images,
error,
isLoading,
} = useQuery({
queryKey: ["images"],
queryFn: getAllImages,
});
useEffect(() => {
if (!api) return;
setCount(api.scrollSnapList().length);
setCurrent(api.selectedScrollSnap() + 1);
api.on("select", () => {
setCurrent(api.selectedScrollSnap() + 1);
});
}, [api]);
if (isLoading) {
return (
<div className="flex justify-center items-center h-screen">
<Loader2 className="h-12 w-12 animate-spin text-primary" />
</div>
);
}
if (error || !images || images.length === 0) {
return (
<Alert variant="destructive" className="m-4">
<AlertCircle className="h-4 w-4" />
<AlertTitle>Error</AlertTitle>
<AlertDescription>
Hubo un problema al cargar las imágenes. Por favor, intenta de nuevo
más tarde.
</AlertDescription>
</Alert>
);
}
return (
<div className="h-screen w-full relative">
<Carousel setApi={setApi} className="h-full">
<CarouselContent className="h-full">
{images.map((image, index) => (
<CarouselItem key={image.$id} className="h-full">
<div className="relative h-full w-full">
<Image
src={image.url}
alt={image.alt}
priority={index === 0}
loading={index === 0 ? "eager" : "lazy"}
className="object-cover"
sizes="100vw"
fill
style={{
objectFit: 'cover'
}}
/>
<div className="absolute inset-0 bg-black bg-opacity-40 flex flex-col justify-end p-6 text-white">
<h2 className="text-2xl md:text-4xl font-bold mb-2">
{image.titulo}
</h2>
<p className="text-sm md:text-lg">{image.descripcion}</p>
</div>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious className="absolute left-4 top-1/2 -translate-y-1/2" />
<CarouselNext className="absolute right-4 top-1/2 -translate-y-1/2" />
</Carousel>
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 bg-black bg-opacity-50 text-white px-4 py-2 rounded-full">
{current} / {count}
</div>
</div>
);
}
According to the NextJS documentation, I should be able to use fill to fill the carousel. Any suggestions?
When using fill the image will take its parent height and width and its bubbling up to this div class="overflow-hidden"
By default, div has 0 height until it takes the height of elements inside it but in our case the image want to inherit the height of its parent which caused the bug.
Without something likes position: absolute that div won't take the height of its parent.
One way to fix this is to change display of the carousel to grid because grid items always fillout their parents height evenly
<div className="h-screen w-full relative" style={{ padding: 150 }}>
<Carousel className="h-full grid">
<CarouselContent className="h-full">
{imgs.map((i) => (
<CarouselItem key={i} className="h-full">
<div className="relative h-full w-full">
{/* <Image src={i} alt="dog pic" width="100" height="100" /> */}
<Image
src={i}
alt="dog pic"
fill
style={{
objectFit: 'cover',
}}
/>
</div>
</CarouselItem>
))}
</CarouselContent>
<CarouselPrevious />
<CarouselNext />
</Carousel>
</div>
Working example: https://stackblitz.com/edit/stackblitz-starters-vhrqbu?file=app%2Fpage.tsx