'Maintain aspect ratio of image with full width in React Native

I have a query regarding tag. I want an image to take entire width of parent which I do using alignSelf:stretch, but I also want the height to be according to the aspect ratio of the image. How can I achieve something like this?

So I want a way to specify the height as a ratio of the width of the Image.



Solution 1:[1]

Use style={{ aspectRatio: 3/2 }} for a horizontal image with width to height ratio of 3:2.

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

(Available in RN 0.40+)

Solution 2:[2]

<Image
   source={require('../../assets/img/headers/image-1.jpg')}
   style={styles.responsiveImage}
 />

const styles = StyleSheet.create({

  responsiveImage: {
    width: '100%',
    // Without height undefined it won't work
    height: undefined,
    // figure out your image aspect ratio
    aspectRatio: 135 / 76,
  },

});

Solution 3:[3]

I like bdv's approach and I use this kind of images almost everywhere in my app. That's why I created an own component which is using onLayout to also support device rotation.

import resolveAssetSource from "resolveAssetSource";
import React, { useCallback, useState } from "react";
import { Image, View } from "react-native";

export default function FullWidthImage(props) {
  const [width, setWidth] = useState(0);
  const [height, setHeight] = useState(0);

  const onLayout = useCallback((event) => {
    const containerWidth = event.nativeEvent.layout.width;

    if (props.ratio) {
      setWidth(containerWidth);
      setHeight(containerWidth * props.ratio);
    } else if (typeof props.source === "number") {
      const source = resolveAssetSource(props.source);

      setWidth(containerWidth);
      setHeight(containerWidth * source.height / source.width);
    } else if (typeof props.source === "object") {
      Image.getSize(props.source.uri, (w, h) => {
        setWidth(containerWidth);
        setHeight(containerWidth * h / w);
      });
    }
  }, [props.ratio, props.source]);

  return (
    <View onLayout={onLayout}>
      <Image
        source={props.source}
        style={{ width, height }} />
    </View>
  );
}

You can use it like this:

<FullWidthImage source={{ uri: "http://example.com/image.jpg" }} />
<FullWidthImage source={require("./images/image.jpg")} />

Or if you know the ratio like this:

<FullWidthImage source={{ uri: "http://example.com/image.jpg"}} ratio={0.5} />
<FullWidthImage source={require("./images/image.jpg")} ratio={0.5} />

Solution 4:[4]

It's actually pretty simple.

The Image class has a getSize method. [1]

Let's say that you've created a component for your aspectRatioImage and you calculate the appropriate values every time componentWillMount fires.

Then your code would look something like this:

componentDidMount() {
    Image.getSize(this.props.source.uri, (srcWidth, srcHeight) => {
      const maxHeight = Dimensions.get('window').height; // or something else
      const maxWidth = Dimensions.get('window').width;

      const ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
      this.setState({ width: srcWidth * ratio, height: srcHeight * ratio });
    }, error => {
      console.log('error:', error);
    });
  }

So now that the image height and width are saved in your component's state, you can just run

 <Image
   style={{ width: this.state.width, height: this.state.height }}
   source={this.props.source}
   resizeMode="cover"
 />

[1] - https://facebook.github.io/react-native/docs/image.html#getsize

Solution 5:[5]

You can use react-native-scalable-image. The following example will do the job:

import React from 'react';
import { Dimensions } from 'react-native';
import Image from 'react-native-scalable-image';

const image = <Image width={Dimensions.get('window').width} source={{uri: '<image uri>'}} />;

Solution 6:[6]

You can calculate the image height based on the width/height ratio.

So if the image originally is 200x100, after setting its resizeMode to stretch:

var deviceWidth: Dimensions.get('window').width;

...

myImage {
    width: deviceWidth,
    height: deviceWidth * 0.5
}

I know maybe it is not the best practice, but it helped me a lot with images of all sizes that needed to mantain a certain relation with other images, etc.

Solution 7:[7]

Typically, doing the following would give us an image rendered to max width/height depending on orientation while maintaining the aspect ratio of the image itself:

render(){
    return(<Image style={{'width': Dimensions.get('window').width, 
                         'height': Dimensions.get('window').height}}
                  resizeMode='contain'
                  source='[URL here]'
           </Image>);
}

Using 'contain' with resizeMode: Scale the image uniformly (maintain the image's aspect ratio) so that both dimensions (width and height) of the image will be equal to or less than the corresponding dimension of the view (minus padding).

Update: * Unfortunately, it seems that there is a common bug with resizeMode's 'contain' specifically when using react native for Android: https://github.com/facebook/react-native/pull/5738*

Solution 8:[8]

In my case i also had to set height to 'auto' :

{
    width: 200,
    height: 'auto',
    aspectRatio: 16 / 9,
}

Solution 9:[9]

In my case, I am using Styled Components in my React Native (v0.62+) project.

I needed to specify a square aspectRatio for Image components that had a defined width and undefined height.

I found that styling height:0; achieved the "square image" result that I wanted:

// Gallery container styled-component
const Gallery = styled.View`
  flexDirection:row;
  flexWrap:wrap;
`

// Square half-width image styled-component
const Photo = styled.Image`
  width:50%;
  height:0;
  aspectRatio:1;
`

This method also works for full width image styling - replacing width:50% with width:100% produces the expect result with correct aspect ratio of each image.

Solution 10:[10]

const RespImage = ({ offer }) => {

const [height, setHeight] = useState(0);
const [width, setWidth] = useState(0);

    let image_url = `YOUR_IMAGE_URI`;

    Image.getSize(image_url, (srcWidth, srcHeight) => {

        const maxHeight = Dimensions.get('window').height;
        const maxWidth = Dimensions.get('window').width;

        const ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
        setHeight(srcHeight * ratio);
        setWidth(srcWidth * ratio);
    });

    return (
        <View>
            <Image resizeMode={'contain'} style={{ height: height, width: width, marginBottom: 20, resizeMode: "contain" }}
                source={{ uri: image_url }}
            />
        </View>
    )

}

Solution 11:[11]

With resizeMode='contain' and flex=1 I get the image in full width while keeping the aspect ratio.

  <Image
     source={{ uri: 'URI' }}
     resizeMode="contain"
     style={{flex:1}} />

The image need to be in a container View with flex or height defined, so the flex of the image can work.

Solution 12:[12]

I tried the Image.getSize approach, but had problems, since we gather all the image links in a config file and then pass the ImageURISource into the source prop of the Image.

My solution for that was to wait for the Image onLayout callback to get it's layout properties and use that to update the dimensions. I created a component for that:

import * as React from 'react';
import { Dimensions, Image, ImageProperties, LayoutChangeEvent, StyleSheet, ViewStyle } from 'react-native';

export interface FullWidthImageState {
  width: number;
  height: number;
  stretched: boolean;
}

export default class FullWidthImage extends React.Component<ImageProperties, FullWidthImageState> {
  constructor(props: ImageProperties) {
    super(props);

    this.state = { width: 100, height: 100, stretched: false };
  }

  render() {
    return <Image {...this.props} style={this.getStyle()} onLayout={this.resizeImage} />;
  }

  private resizeImage = (event: LayoutChangeEvent) => {
    if (!this.state.stretched) {
      const width = Dimensions.get('window').width;
      const height = width * event.nativeEvent.layout.height / event.nativeEvent.layout.width;
      this.setState({ width, height, stretched: true });
    }
  };

  private getStyle = (): ViewStyle => {
    const style = [StyleSheet.flatten(this.props.style)];
    style.push({ width: this.state.width, height: this.state.height });
    return StyleSheet.flatten(style);
  };
}

This will update the dimensions of the image to match the width of the screen.