skip to Main Content

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?

3

Answers


  1. to be able to use the fill with object-fit: cover properties your parent div should have relative with height and width classes then it will be fixed.

    Login or Signup to reply.
  2. 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 this 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

    Login or Signup to reply.
  3. The issue with the fill property in the Next.js Image component is that it requires the parent element to have both position: relative and a defined height. Without these, the image won’t render correctly.

    To resolve this:

    1. Ensure the parent element of the Image has position: relative and its height is defined:
    <div className="relative h-full w-full">
        <Image
            src={image.url}
            alt={image.alt}
            fill
            className="object-cover"
            sizes="100vw"
        />
    </div>
    
    1. Ensure all ancestor elements, like CarouselItem and CarouselContent, also have a defined height:
    <CarouselItem className="h-full">
    
    1. If the images still don’t appear, inspect the elements using your browser’s developer tools to ensure that all parent containers have a valid height.

    This should allow the images to display correctly using the fill property.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search