111

I am trying to display list of images in a scrollview. Width should be 100%, while height should be automatic, keeping aspect ratio.

The searches I did pointed to various solutions which give fullscreen background style.

const styles = StyleSheet.create({
    image: {
        width: null,
        height: 300,
        resizeMode: 'cover'
    }
});

<ScrollView style={{flex: 1}}>
    <Image style={styles.image} source={require('../../../images/collection-imag1.png')}/>
    <Image style={styles.image} source={require('../../../images/collection-imag2.png')}/>
</ScrollView>

I have tried various combinations of width: null, height: null, flex: 1, alignSelf etc. The above solution is almost working, except the height is not dynamic. Parts of the image are not visible.

Prabhakar Bhat
  • 1,323
  • 2
  • 10
  • 15

14 Answers14

95

So after thinking for a while I was able to achieve height: auto in react-native image. You need to know the dimensions of your image for this hack to work. Just open your image in any image viewer and you will get the dimensions of the your image in file information. For reference the size of image I used is 541 x 362

First import Dimensions from react-native

import { Dimensions } from 'react-native';

then you have to get the dimensions of the window

const win = Dimensions.get('window');

Now calculate ratio as

const ratio = win.width/541; //541 is actual image width

now the add style to your image as

imageStyle: {
    width: win.width,
    height: 362 * ratio, //362 is actual height of image
}
Vinit Kadam
  • 1,019
  • 1
  • 8
  • 8
56

"resizeMode" isn't style property. Should move to Image component's Props like below code.

const win = Dimensions.get('window');

const styles = StyleSheet.create({
    image: {
        flex: 1,
        alignSelf: 'stretch',
        width: win.width,
        height: win.height,
    }
});

...
    <Image 
       style={styles.image}
       resizeMode={'contain'}   /* <= changed  */
       source={require('../../../images/collection-imag2.png')} /> 
...

Image's height won't become automatically because Image component is required both width and height in style props. So you can calculate by using getSize() method for remote images like this answer and you can also calculate image ratio for static images like this answer.

There are a lot of useful open source libraries -

lwinkyawmyat
  • 1,221
  • 1
  • 16
  • 34
  • 6
    It is a style property on image, according to https://facebook.github.io/react-native/docs/image.html. Changing it to property did not work either. – Prabhakar Bhat Sep 22 '16 at 07:27
  • 7
    this does not answer the given question of having height to be automatically calculated – chrmod May 07 '18 at 20:56
  • This works, but you should say that you need to "import { Dimensions } from 'react-native';" – Sabba Keynejad Dec 04 '18 at 15:12
  • 3
    This does not answer the question in so many angles. Apart from what's said in the above comments, [the doc](https://facebook.github.io/react-native/docs/image#getsize) explicitly says getSize() "does not work for static image resources". – Betty Apr 30 '19 at 05:54
  • @Betty I think getSize() does work for static images. – kojow7 Jan 14 '20 at 05:28
51

I've found a solution for width: "100%", height: "auto" if you know the aspectRatio (width / height) of the image.

Here's the code:

import { Image, StyleSheet, View } from 'react-native';

const ResponsiveImage = () => (
    <View style={styles.imgContainer}>
        <Image style={styles.image} source={require('assets/images/image.png')} />
    </View>
);

const style = StyleSheet.create({
    imgContainer: {
        flexDirection: 'row'
    },
    image: {
        resizeMode: 'contain',
        flex: 1,
        aspectRatio: 1 // Your aspect ratio
    }
});

This is the most simplest way I could get it to work without using onLayout or Dimension calculations. You can even wrap it in a simple reusable component if needed. Give it a shot if anyone is looking for a simple implementation.

Arafat Zahan
  • 719
  • 5
  • 7
  • 1
    This is definitely the right mechanism. From the react-native docs: "On a node with a set flex basis, aspect ratio controls the size of the node in the cross axis if unset". If the parent is a row, then aspectRatio will set the height, but if it's a column, it will affect the width. – cdosborn Mar 24 '23 at 17:15
  • 1
    You are officially my hero. – mdmb Jul 24 '23 at 20:33
14

use aspectRatio property in style

Aspect ratio control the size of the undefined dimension of a node. Aspect ratio is a non-standard property only available in react native and not CSS.

  • On a node with a set width/height aspect ratio control the size of the unset dimension
  • On a node with a set flex basis aspect ratio controls the size of the node in the cross axis if unset
  • On a node with a measure function aspect ratio works as though the measure function measures the flex basis
  • On a node with flex grow/shrink aspect ratio controls the size of the node in the cross axis if unset
  • Aspect ratio takes min/max dimensions into account

docs: https://reactnative.dev/docs/layout-props#aspectratio

try like this:

import {Image, Dimensions} from 'react-native';

var width = Dimensions.get('window').width; 

<Image
    source={{
        uri: '<IMAGE_URI>'
    }}
    style={{
        width: width * .2,  //its same to '20%' of device width
        aspectRatio: 1, // <-- this
        resizeMode: 'contain', //optional
    }}
/>
11

I had problems with all above solutions. Finally i used aspectRatio to do the trick. When you know image width and height and they are large, just calculate aspectRatio and add it to image like:

<PhotoImage
    source={{uri: `data:image/jpeg;base64,${image.base64}`}}
    style={{ aspectRatio: image.width / image.height }}
 />

AspectRatio is a layout property of React Native, to keep aspect ratio of image and fit into parent container: https://facebook.github.io/react-native/docs/layout-props#aspectratio

Łukasz Blaszyński
  • 1,536
  • 1
  • 8
  • 13
  • 2
    This is the only real answer. To extend on that. If you are requesting the image from the network. You can get the size using `Image.getSize` static method from `react-native` – sospedra Oct 02 '19 at 08:47
7

Right click on you image to get resolution. In my case 1233 x 882

const { width } = Dimensions.get('window');

const ratio = 882 / 1233;

    const style = {
      width,
      height: width * ratio
    }

<Image source={image} style={style} resizeMode="contain" />

That all

6

You always have to set the width and height of an Image. It is not going to automatically size things for you. The React Native docs says so.

You should measure the total height of the ScrollView using onLayout and set the height of the Images based on it. If you use resizeMode of cover it will keep the aspect ratio of your Images but it will obviously crop them if it's bigger than the container.

rclai
  • 1,880
  • 14
  • 25
  • 9
    Automatic sizing only doesn't work for remote images. OP is referencing a local, static image file, which in the words of React Native _"can be automatically sized because their dimensions are available immediately at the time of mounting."_ – PowerOfM Oct 06 '17 at 05:52
5

Solution April 2020:

So the above answers are cute but they all have (imo) a big flaw: they are calculating the height of the image, based on the width of the user's device.

To have a truly responsive (i.e. width: 100%, height: auto) implementation, what you really want to be doing, is calculating the height of the image, based on the width of the parent container.

Luckily for us, React Native provides us with a way to get the parent container width, thanks to the onLayout View method.

So all we need to do is create a View with width: "100%", then use onLayout to get the width of that view (i.e. the container width), then use that container width to calculate the height of our image appropriately.

 

Just show me the code...

The below solution could be improved upon further, by using RN's Image.getSize, to grab the image dimensions within the ResponsiveImage component itself.

JavaScript:

// ResponsiveImage.ts

import React, { useMemo, useState } from "react";
import { Image, StyleSheet, View } from "react-native";

const ResponsiveImage = props => {
  const [containerWidth, setContainerWidth] = useState(0);
  const _onViewLayoutChange = event => {
    const { width } = event.nativeEvent.layout;
    setContainerWidth(width);
  }

  const imageStyles = useMemo(() => {
    const ratio = containerWidth / props.srcWidth;
    return {
      width: containerWidth,
      height: props.srcHeight * ratio
    };
  }, [containerWidth]);

  return (
    <View style={styles.container} onLayout={_onViewLayoutChange}>
      <Image source={props.src} style={imageStyles} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: { width: "100%" }
});

export default ResponsiveImage;


// Example usage...

import ResponsiveImage from "../components/ResponsiveImage";

...

<ResponsiveImage
  src={require("./images/your-image.jpg")}
  srcWidth={910} // replace with your image width
  srcHeight={628} // replace with your image height
/>

 

TypeScript:

// ResponsiveImage.ts

import React, { useMemo, useState } from "react";
import {
  Image,
  ImageSourcePropType,
  LayoutChangeEvent,
  StyleSheet,
  View
} from "react-native";

interface ResponsiveImageProps {
  src: ImageSourcePropType;
  srcWidth: number;
  srcHeight: number;
}

const ResponsiveImage: React.FC<ResponsiveImageProps> = props => {
  const [containerWidth, setContainerWidth] = useState<number>(0);
  const _onViewLayoutChange = (event: LayoutChangeEvent) => {
    const { width } = event.nativeEvent.layout;
    setContainerWidth(width);
  }

  const imageStyles = useMemo(() => {
    const ratio = containerWidth / props.srcWidth;
    return {
      width: containerWidth,
      height: props.srcHeight * ratio
    };
  }, [containerWidth]);

  return (
    <View style={styles.container} onLayout={_onViewLayoutChange}>
      <Image source={props.src} style={imageStyles} />
    </View>
  );
};

const styles = StyleSheet.create({
  container: { width: "100%" }
});

export default ResponsiveImage;


// Example usage...

import ResponsiveImage from "../components/ResponsiveImage";

...

<ResponsiveImage
  src={require("./images/your-image.jpg")}
  srcWidth={910} // replace with your image width
  srcHeight={628} // replace with your image height
/>
  • 2
    As you suggested https://github.com/vivaxy/react-native-auto-height-image does great work for the issue. Thank – Ajay Sep 19 '20 at 13:19
3

For image tag you can use this type of style, it worked for me:

imageStyle: {
    width: Dimensions.get('window').width - 23,
    resizeMode: "contain",
    height: 211,
 },
Alex Riabov
  • 8,655
  • 5
  • 47
  • 48
kallayya Hiremath
  • 3,560
  • 1
  • 13
  • 14
3

Let me share what I end up with, which allows to set correctly width or height by getting the image dimensions. In addition, the code allows to fetch a loading image while the large image data we need is being transfered:

  1. Use static method Image.prefetch to have the image downloaded and available to cache.

  2. Use static method Image.getSize to collect height and width and use it to compute an aspect ratio and then the final height (or width)

  3. Display image with a default style to your prefered width (The height will be computed with aspect ratio kept)

     function ImageX(props: {source: string, id: string})
     {
       const [imageHeight, setImageHeight] = React.useState(1);
       Image.prefetch(props.source)
       .then(() => {
           Image.getSize(props.source, (width, height) => {
             let aspectRatio =  height/width;
             setImageHeight(aspectRatio*Dimensions.get('window').width);
           });
       })
       .catch(error => console.log(error))
       if (imageHeight <=1) //Image not in cache (disk) yet
         {
           return (
             <Image key={props.id} style={styleimg.image} source={{uri: 'http://www.dsdsd/loaderpreview.gif'}}/>
           );
       }
       else
       {
         return (
           <Image key={props.id} style={styleimg.image} height={imageHeight} source={{uri: props.source}}/>
         );
       }
     }
    

    const styleimg = StyleSheet.create({ image: { width: Dimensions.get('window').width, resizeMode: 'contain' //... // you can set a height defaults } });

Thomas Bones
  • 53
  • 1
  • 4
2

this may help for auto adjusting the image height having image 100% width

image: { width: "100%", resizeMode: "center" "contain", height: undefined, aspectRatio: 1, }

0

I took Jon James' idea and applied it on the image itself, instead of the image's container. This is what I ended up using, and it works great:

import { useState, useEffect } from 'react';
import { Image } from 'react-native';

export default function AutoHeightImage({ uri, style }) {
  const [paintedWidth, setPaintedWidth] = useState(0);
  const [resultHeight, setResultHeight] = useState(0);

  useEffect(() => {
    let stillMounted = true;
    Image.getSize(uri, (realW, realH) => {
      if (!paintedWidth || !stillMounted) return;
      const shrinkRatio = realW / paintedWidth;
      setResultHeight(realH / shrinkRatio);
    });
    return () => (stillMounted = false);
  }, [paintedWidth]);

  return (
    <Image
      style={[{ width: '100%' }, style, { height: resultHeight }]}
      source={{ uri }}
      onLayout={(event) => setPaintedWidth(event.nativeEvent.layout.width)}
    />
  );
}
0

Try using Image.getSize(uri, (width, height) => {console.log(width, height);});.

const Image = url => {
  const [aspectRatio, setAspectRatio] = useState(1.2);
  Image.getSize(
    url,
    (width, height) => {
      setAspectRatio(width / height);
    },
  );
  return (
    <Image
      source={{
        uri: url
      }}
      style={{width: '100%', aspectRatio: aspectRatio}}
      resizeMode="cover"
    />
  );
};
-2

function ImageX( props){
   const [state, setState] = React.useState({v:{height:1}});
   useEffect(() => {
    if(!props.source || !props.source.uri) return;
    Image.prefetch(props.source.uri)
    .then(() => {
        Image.getSize(props.source.uri, (width, height) => {
          let aspectRatio =  height/width;
          state.v.width=Dimensions.get('window').width;
          state.v.height=aspectRatio*state.v.width;
          setState({...state});
        });
    })
    .catch(error => console.log(error))
   },[props.source && props.source.uri]);
   
   if (state.v.height <=1) //Image not in cache (disk) yet
     {
      return (
        <Image {...props}/>
      );
   }
   else
   {
     let neededst={v:{width:state.v.width, height:state.v.height}}
     let st={v:props.style};
    assignObject(neededst, st);
     return (
       <Image {...props} style={neededst.v}/>
     );
   }
 }

 function assignObject(target, source) {
  if (!source) return;
  for (let k in target) {
    let v = target[k];
    if (Object(v) === Object) assignObject(v, source[k]);
    else {
      if (source[k]) {
        try {
          Object.assign(v, source[k]);
        } catch (e) {
          alert(e);
        }
      }
    }
  }

  for (let k in source) {
    if (!target[k]) {
      target[k] = source[k];
    }
  }

}

Using Image component from https://reactnativeelements.com/docs/image

Sumit Garai
  • 1,205
  • 8
  • 6
  • This is the only working solution for dynamic images! Everyone is suggesting that we want to show the same aspect ratio every time. But my images are on different sizes and this is the only working solution for me! – Sick Ranchez Apr 14 '22 at 13:42