Prabhakar Bhat
Prabhakar Bhat

Reputation: 1443

How to set image width to be 100% and height to be auto in react native?

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.

Upvotes: 123

Views: 216415

Answers (14)

Arafat Zahan
Arafat Zahan

Reputation: 829

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.

Upvotes: 62

user19247721
user19247721

Reputation:

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"
    />
  );
};

Upvotes: 0

Nitsan BenHanoch
Nitsan BenHanoch

Reputation: 679

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)}
    />
  );
}

Upvotes: 1

Sumit Garai
Sumit Garai

Reputation: 1331

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

Upvotes: -2

Thomas Bones
Thomas Bones

Reputation: 53

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 } });

Upvotes: 3

Dmitry Preeternal
Dmitry Preeternal

Reputation: 378

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

Upvotes: 7

sumukha hegde
sumukha hegde

Reputation: 85

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

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

Upvotes: 2

Jon James Designs
Jon James Designs

Reputation: 105

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
/>

Upvotes: 5

Tofa Maulana Irvan
Tofa Maulana Irvan

Reputation: 179

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
    }}
/>

Upvotes: 17

Łukasz Blaszyński
Łukasz Blaszyński

Reputation: 1565

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

Upvotes: 12

lwinkyawmyat
lwinkyawmyat

Reputation: 1242

"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 -

Upvotes: 58

kallayya Hiremath
kallayya Hiremath

Reputation: 4488

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,
 },

Upvotes: 3

Vinit Kadam
Vinit Kadam

Reputation: 1099

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
}

Upvotes: 102

rclai
rclai

Reputation: 1890

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.

Upvotes: 6

Related Questions