Component for Automatically Scaling Image Height in React Native Based on Its Width

Cover Image for Component for Automatically Scaling Image Height in React Native Based on Its Width

In React Native, when you need to create a layout for different mobile devices, you usually use relative units to set the width of images. However, the height is not calculated automatically, which can lead to layout issues.

To ensure that the height of the image container maintains the proportions of the image itself, we can use an approach from web development.

We will create a container where we set the paddingTop property in percentages. Since the paddingTop value is calculated relative to the width of the element it is set on, we can use this approach.

To calculate the aspect ratio of the original image, we can use the following formula:

const aspectRatio = height / (width / 100);

Where height is the height of the original image, and width is its width.

We will use the aspectRatio value to set the paddingTop of our container. This way, the container will have the same height-to-width ratio as the original image, with width and height values specified in percentages, allowing this container to be embedded in any layout.

To place the image inside this container, we can use the following approach:

<View style={{ position: 'relative', paddingTop: `${aspectRatio}%` }}>
  <View style={{ position: 'absolute', left: 0, right: 0, top: 0, bottom: 0 }}>
    <Image source={imageSource} style={{ width: '100%', height: '100%' }} />
  </View>
</View>

Here, aspectRatio is the value we calculated earlier. imageSource is the source of the image.

This way, the image will automatically scale its height relative to the width of the container while maintaining its proportions.

Here is the complete component code:

import React from 'react';
import {Image, ImageProps, ImageResizeMode, StyleSheet, View, ViewProps} from 'react-native';

interface ScaleImageProps {
  /**
   * If you don’t need the image width as a percentage and automatic height scaling,
   * then perhaps you need to use the Image component
   */
  containerWidth: `${number}%` | number;
  imageWidth: number;
  imageHeight: number;
  source: ImageProps['source'];
  containerStyle?: ViewProps['style'];
  resizeMode: ImageResizeMode;
}

export const ScaleImage: React.FC<ScaleImageProps> = ({
  containerWidth,
  imageWidth,
  imageHeight,
  source,
  containerStyle,
  resizeMode
}) => {
  const paddingTop: `${number}%` = `${imageHeight / (imageWidth / 100)}%`;

  return (
    <View
      style={[
        {
          width: containerWidth,
          paddingTop,
        },
        containerStyle,
      ]}>
      <View style={styles.wrapper}>
        <Image style={styles.image} resizeMode={resizeMode} source={source} />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  wrapper: {
    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
    bottom: 0,
  },
  image: {
    width: '100%',
    height: '100%',
  },
});