React Native – How to maintain image ratio with width = “100%”?

I have images uris in my database, and I am rendering them with width: "100%"

As images can be 1080×1080, 750×1080 and 1920×1080, I need to find a way to render them preserving the aspect ratio… I mean, if I have the device’s width is 600px, the image’s width has to be 600px (“100%”), and its height has to be auto adapted, in order to avoid losing its ratio.

I have tried this CSS:

  image: {
    width: "100%",
    aspectRatio: 1,
  },

but only works for squared images (1080×1080). How can I do a height: "auto" in react native, so that the other images are resized with their correct aspect?

Important: I don’t know the dimensions of the images, I only know that the image’s width has to be equals to the device width.

Answer

There is a few step you have to take.

First you need to know the exact size of the image and that you do with Image.getSize or Image.getSizeWithHeaders

let me give an example below how you could do it.

export interface ImageDetli {
  width: number;
  height: number;
  url: string;
}

const ImageRenderer = ({
  url
}: {
  url: string
}) => {
  const [windowWidth, setWindowWidth] = useState(Dimensions.get("window").width);
  const [windowHeight, setWindowHeight] = useState(Dimensions.get("window").height);
  const [loading, setLoading] = useState(true); // untill we load the image fully
  const [image, setImage] = useState(undefined as ImageDetli | undefined)
  const getSize = async(url: string) => {

    return new Promise((resolve, reject) => {
      // this is very imported so you can keep an eye on the size of the image
      Image.getSize(url, (width, height) => {
        resolve({
          url: url,
          width: width > windowWidth ? windowWidth : width,
          height: height > windowHeight ? windowHeight : height
        });
      }, async(error) => {
        console.log("image not loaded: " + url);
        console.log(error)
        resolve(undefined);

      })
    }) as Promise <ImageDetli | undefined>
  }

  useEffect(() => {
    (async() => {
      var imageDetali = await getSize(url);
      if (!imageDetali){
        // load an empty Image or somthing
      }else {
        setImage(imageDetali);
      }
      setLoading(false)
    })();
  }, [])


  return (
  <>
  {loading?(<ActivityIndicator size="large"color="#0000ff"/>)
  :
  (
  <View style={{ width: "100%", height: "100%", alignItems: "center", 
  justifyContent: "center" }}>
  <Image source={{uri: image.url}}
  style={{ width:image.width, height image.height, resizeMode: "contain" }} />
 </View>
  )}
  </>
  )
}

I hop you understand this, you may ask if you need to know more.

Leave a Reply

Your email address will not be published. Required fields are marked *