Search code examples
reactjsnext.jscarouselstrapi

React-responsive-carousel (nextJS/strapi) images are not showing on mobile


I am working on a nextJS/Strapi online store and I want to use the react-responsive-carousel I followed all the steps on [ https://www.npmjs.com/package/react-responsive-carousel ].

(I am working on localhost)

The Carousel works fine on the desktop using the dynamic images from Strapi API (Single type collection) but the images don't show on my phone when I try to access the localhost from there. All other images from Strapi work just fine except the ones inside the Carousel.

Carousel code:

import { imgToUrl } from "../../utils/urls";
import { Carousel } from "react-responsive-carousel";

const index = ({ data }) => {

  const images = data.slider.map((slide) => imgToUrl(slide.image)); //imgToUrl is a function that takes the image URL and concatinate it with HTTP://localhost:1337/

  return (
    <>
      <Carousel
        autoPlay={true}
        emulateTouch={true}
        infiniteLoop={true}
        showThumbs={false}
        width="100%"
      >
        {images.map((image) => (
          <div className="h-full w-full">
            <img className="w-full h-full" src={image} />
            {/* <p className="legend">Legend 1</p> */}
          </div>
        ))}
      </Carousel>
    </>
  );
};

export default index;

imgToUrl Code:

export const API_URL =
  process.env.NEXT_PUBLIC_API_URL || "http://localhost:1337";

/**
 *
 * @param {any} image
 *
 */
export const imgToUrl = (image) => {
  if (!image) {
    return "/Products/3.jpg"; //default image when there is not image uploaded
  }
  if (image.url.indexOf("/") === 0) {
    return `${API_URL}${image.url}`; // Concatinates http://localhost:1337 with the image url
  }
  return image.url;
};

Output of imToUrl :

http://localhost:1337/uploads/banner_3_d93516ad90.jpg

I would appreciate any help. Thank you.


Solution

  • I am also using Strapi "headless" CMS as my backend and GraphQL API to retrieve data from frontend, and below is how I have currently implemented my "Strapi Media" Image component:

    frontend/utils/media.js

    export function getStrapiMedia(url) {
      if (url == null) {
        return null
      }
    
      // Return the full URL if the media is hosted on an external provider
      if (url.startsWith("http") || url.startsWith("//")) {
        return url
      }
    
      // Otherwise prepend the URL path with the Strapi URL
      return `${
        process.env.NEXT_PUBLIC_STRAPI_API_URL || "http://localhost:1337"
      }${url}`
    }
    

    frontend/utils/types.js

    import PropTypes from 'prop-types'
    
    export const linkPropTypes = PropTypes.shape({
      id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
      url: PropTypes.string.isRequired,
      text: PropTypes.string.isRequired,
      newTab: PropTypes.bool,
    })
    
    export const mediaPropTypes = PropTypes.shape({
      id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
      alternativeText: PropTypes.string,
      mime: PropTypes.string.isRequired,
      url: PropTypes.string.isRequired,
    })
    
    export const buttonLinkPropTypes = PropTypes.shape({
      theme: PropTypes.string,
      text: PropTypes.string.isRequired,
      newTab: PropTypes.bool,
    })
    

    frontend/components/elements/image.js

    import { getStrapiMedia } from "utils/media"
    import Image from "next/image"
    import PropTypes from "prop-types"
    import { mediaPropTypes } from "utils/types"
    
    const NextImage = ({ media, ...props }) => {
      const { url, alternativeText } = media
    
      const loader = ({ src }) => {
        return getStrapiMedia(src)
      }
    
      // The image has a fixed width and height
      if (props.width && props.height) {
        return (
          <Image loader={loader} src={url} alt={alternativeText || ""} {...props} />
        )
      }
    
      // The image is responsive
      return (
        <Image
          loader={loader}
          layout="responsive"
          width={media.width}
          height={media.height}
          objectFit="contain"
          src={url}
          alt={alternativeText || ""}
        />
      )
    }
    
    Image.propTypes = {
      media: mediaPropTypes.isRequired,
      className: PropTypes.string,
    }
    
    export default NextImage
    

    And below is an example use case from another component's context:

    ...
    import NextImage from "./image"
    ...
              <div className="flex flex-row items-center">
                <Link href="/">
                  <a className="h-8 w-32">
                    <NextImage layout="fill" className="image" media={navbar.logo} />
                  </a>
                </Link>
              </div>
    ...
    

    I hope this helps - let me know if you could use further source share here.

    I am just learning the Strapi Framework myself, opting to bootstrap my software engineering portfolio, profile, and blog site from Strapi Next Corporate Starter. Are you also using a Strapi bootstrapped project, or custom using their SDK?

    While working on my own custom Carousel I also discovered that if you are implementing your custom component as a section component, you will need to add the import statement and array member to the Strapi defaults in:

    frontend/components/sections.js

    Example from my use case:

    ...
    import CardCarouselGroup from "@/components/sections/card-carousel-group";
    ...
    // Map Strapi sections to section components
    const sectionComponents = {
      "sections.hero": Hero,
      "sections.large-video": LargeVideo,
      "sections.feature-columns-group": FeatureColumnsGroup,
    ...
      "sections.card-carousel-group": CardCarouselGroup
    }
    ...