react-nativeexpoaspect-ratio

Automatic image aspect ratio in react native


I would like to add an image to my app, which is built using the react-native framework with expo.

I want the Image component to:

I tried setting width: 100%. That does not work in react-native. You have to either set the height to non-auto value or use aspectRatio (or perhaps there is another approach I'm not aware of). When I use aspectRatio, the output looks like this:

import { Image } from "expo-image";
import { memo, useState } from "react";
import { View } from "react-native";
import { Text } from "react-native-paper";

const TheImage = require("@/assets/images/tailwind-logo.svg");

export default memo(function MapLoader() {
  return (
    <View>
      <Image source={TheImage} contentFit="cover" style={{ width: "100%", backgroundColor: "red", aspectRatio: 1 }} />
      <Text variant="displayLarge">React Native</Text>
    </View>
  );
});

enter image description here

As you can see, the Image component (highlighted in red) becomes a square (aspectRatio: 1) and takes up unnecessary space. My alternative approach was to use the onLoad prop to update the correct aspectRatio once the image has loaded. That ultimately works, but causes a brief flash because the component is first rendered with aspectRatio: 1.

import { Image } from "expo-image";
import { memo, useState } from "react";
import { View } from "react-native";
import { Text } from "react-native-paper";

const TheImage = require("@/assets/images/tailwind-logo.svg");

export default memo(function MapLoader() {
  const [aspectRatio, setAspectRatio] = useState<number | undefined>(1);
  return (
    <View>
      <Image
        source={TheImage}
        contentFit="cover"
        style={{ width: "100%", backgroundColor: "red", aspectRatio }}
        onLoad={({ source }) => {
          setAspectRatio(source.width / source.height);
        }}
      />
      <Text variant="displayLarge">React Native</Text>
    </View>
  );
});

enter image description here

My question: how can I render the image so that:


Solution

  • The best solution is to get the image size and set the ratio dynamically. Your code can look something like this for full with and dynamic height:

    import React from 'react';
    import { Image, View } from 'react-native';
    
    export default function FullWidthLocalImage() {
      const source = require('./assets/image.png');
      const { width, height } = Image.resolveAssetSource(source);
      const aspectRatio = width / height;
    
      return (
        <View style={{ width: '100%' }}>
          <Image
            source={source}
            style={{
              width: '100%',
              aspectRatio,
            }}
            resizeMode="cover"
          />
        </View>
      );
    }
    

    For remote images you can use Image.getSize