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