React Native component for scaling an Image within the parent View
npm install react-native-responsive-image-view_React Native component for scaling an Image within the parent View_
[![npm Version][npm-image]][npm-url] [![Build Status][ci-image]][ci-url]
[![Code Coverage][coverage-image]][coverage-url]
You want to display an image in your React Native app that fills the width of its container and
scales its height according to the aspect ratio of the image. If you're coming from front-end web
development, you may be familiar with Bootstrap 3's img-responsive class or manually applyingmax-width: 100% and height: auto to an image. Unfortunately, auto is not a valid value forheight in React Native, so that technique doesn't quite translate.
This calculates the aspect ratio of your image for you (or uses a fixed value, if you supply one)
and provides you with the appropriate props to apply to a View container and an Image inside it
which will produce the results you're looking for. The secret sauce is setting both the height andwidth attributes of the style prop on the Image to 100% and wrapping it with a View that
has its [aspectRatio][aspectratio] style property set to match the aspect ratio you want. The
package provides both a [render prop component][render-props] and a [custom hook][custom-hooks].
- Installation
- Usage
- Component
- Hook
- Input
- Basic Inputs
- onLoad
- onError
- source
- Advanced Inputs
- aspectRatio
- Component-only Inputs
- Render Prop Function
- component
- render
- children
- Output
- prop getters
- getViewProps
- getImageProps
- state
- utils
- Examples
- Snack Playground
- Inspiration
- Other Solutions
- LICENSE
Using [npm][npm]:
``shell`
npm install react-native-responsive-image-view
> This package also depends on react and react-native. Please make sure you have those installed
> as well.
As mentioned above, this package includes both a render prop component (the default export) and a
custom hook (a named export). They provide the same functionality, so choose whichever is most
appropriate for your project.
`tsx
import * as React from 'react';
import { Image, View } from 'react-native';
import { ResponsiveImageView } from 'react-native-responsive-image-view';
function MyComponent({ imageUri }) {
return (
{({ getViewProps, getImageProps }) => (
)}
);
}
`
N.B. This component doesn't render anything itself, it just calls your render function or
injected component and renders that. ["Use a render prop!"][use-a-render-prop] Just be sure to
render the Image inside the View in your render function.
> In addition to a literal render prop, it also supports the component injection and
> function-as-children patterns if you prefer. See the Render Prop Function
> section for details.
`tsx
import * as React from 'react';
import { Image, View } from 'react-native';
import { useResponsiveImageView } from 'react-native-responsive-image-view';
function MyComponent({ imageUri }) {
const { getViewProps, getImageProps } = useResponsiveImageView({ source: { uri: imageUri } });
return (
);
}
`
The component takes its inputs as individual props, while the hook takes its inputs as an object
(the only parameter), but the inputs themselves are primarily the same:
#### onLoad
> function() | optional, no useful default
Called after the image has been loaded (and the aspect ratio has been calculated).
#### onError
> function(error: string) | optional, no useful default
Called if the image could not be loaded. Called with the error message in the form of a string.
- error: the error message as a string
#### source
> number/object | _required_
The source for your Image. This can be a local file resource (the result of an import orrequire statement) or an object containing a uri key with a remote URL as its value.
#### aspectRatio
> number | control prop, default: automatically calculated from image dimensions
By default, react-native-responsive-image-view manages this value internally based on the
dimensions of the source image. However, if more control is needed, you can pass in a fixed aspect
ratio to use instead. This is useful if you want to fit the image into a statically shaped box such
as a navigation drawer header.
#### Render Prop Function
This is where you render whatever you want to based on the state of
react-native-responsive-image-view when using the component (not applicable when using the hook).
It's just a function or component, available in a few different ways. Read Donavon West's very
opinionated but informative [post about them][faccs-and-ci] for more information. They all receive
the same props, so it is purely a stylistic choice left up to you as the consumer.
`tsx
// component injection
// render prop
// function-as-children
{/ right here /}
`
Choose your approach by passing one of the following props:
##### component
> component | _optional_
This is rendered with an object passed in as props. Read more about the properties of this object
in the Output section.
##### render
> function({}) | _optional_
This is called with an object. Read more about the properties of this object in the
Output section.
##### children
> function({}) | _optional_
This is called with an object. Read more about the properties of this object in the
Output section.
N.B. Multiple render methods should not be combined, but in the event that they are,
react-native-responsive-image-view will honor the following order:
1. componentrender
1. children
1. function as children
1. non-functional (render children normally)null
1. (render nothing)
Regardless of whether you are using the component or the hook, the results are an object containing
important properties you'll need for rendering. It will be passed to the
Render Prop Function when using the component, and returned from the hook
invocation when using the hook. The properties of this object can be split into the following
categories:
> See [this blog post about prop getters][kent-prop-getters]
These functions are used to apply props to the elements that you render. This gives you maximum
flexibility to render what, when, and wherever you like. You call these on the element in question
(for example:
rather than applying them on the element yourself to avoid your props being overridden (or
overriding the props returned). For example: getViewProps({ hitSlop: myHitSlop }).
| property | type | description |
| --------------- | -------------- | ------------------------------------------------------------------------------- |
| getViewProps | function({}) | returns the props you should apply to the View (parent of Image) you render |getImageProps
| | function({}) | returns the props you should apply to the Image (child of View) you render |
#### getViewProps
This method should be applied to the View you render. It is recommended that you pass all props asView
an object to this method which will compose together any of the props you need to apply to the while preserving the ones that react-native-responsive-image-view needs to apply to makeView
the behave.
There are no required properties for this method.
#### getImageProps
This method should be applied to the Image you render. It is recommended that you pass all propsImage
as an object to this method which will compose together any of the props you need to apply to the while preserving the ones that react-native-responsive-image-view needs to apply to makeImage
the behave.
There are no required properties for this method.
These are values that represent the current state of the component.
| property | type | description |
| --------- | --------- | -------------------------------------------------------------- |
| loading | boolean | whether or not the image is currently loading |error
| | string | an error message if the image failed to load ('' on success) |
These are miscellaneous helpers that don't fit in the other major categories.
| property | type | description |
| -------- | ------------ | ----------------------------------------------------- |
| retry | function() | force another attempt to resolve the image dimensions |
See the [examples][examples-directory] directory for examples using both the component and the
hook.
Check out the [Snack Playground][snack-playground] for an interactive experience where you can try
out the various usage patterns. You can see the code run live on your own device via [Expo
Go][expo-go], or enable the Preview option to use the in-browser simulators!
> Note: Snack's underlying build system, snackager,
> cannot currently handle some TypeScript features used
> in this library, so the version showcased in the playground is pinned to the latest pre-TypeScript
> implementation. We hope the Expo team can resolve this soon, but it should still provide a useful
> environment to try out the library in the meantime.
I was heavily inspired by [react-native-flex-image][react-native-flex-image] from
[KodeFox][kodefox] (see the Other Solutions section) with regards to how to
display the image to get the desired behavior. The original implementation and API were primarily
inspired by [Michael Jackson][mjackson]'s ["Use a Render Prop!"][use-a-render-prop] post and video,
as well as [Kent C. Dodds][kentcdodds]' [introduction to prop getters][kent-prop-getters].
After scouring [npm][npm] for solutions and trying most (if not all) of them, the best option I
found was the following:
- [react-native-flex-image][react-native-flex-image]
Unfortunately, it is too restrictive for my use cases. It renders the magic onPress
combination for you, preventing you from customizing the layout. For example, it provides an prop if you want to make the Image touchable, but it wraps the Image in aTouchableOpacity (what if I wanted a TouchableHighlight instead?). It also renders its ownActivityIndicator
loading indicator (an ) as well as error messages (as Text`). At the end of the
day, these features proved to be too opinionated.
[MIT][license]
[npm-image]: https://img.shields.io/npm/v/react-native-responsive-image-view.svg?style=flat-square
[npm-url]: https://www.npmjs.com/package/react-native-responsive-image-view
[ci-image]:
https://img.shields.io/github/actions/workflow/status/wKovacs64/react-native-responsive-image-view/ci.yml?logo=github&style=flat-square
[ci-url]:
https://github.com/wKovacs64/react-native-responsive-image-view/actions?query=workflow%3Aci
[coverage-image]:
https://img.shields.io/codecov/c/github/wKovacs64/react-native-responsive-image-view/main.svg?style=flat-square
[coverage-url]: https://codecov.io/gh/wKovacs64/react-native-responsive-image-view/branch/main
[aspectratio]: https://facebook.github.io/react-native/docs/layout-props#aspectratio
[render-props]:
https://react.dev/reference/react/Children#calling-a-render-prop-to-customize-rendering
[custom-hooks]: https://react.dev/learn/reusing-logic-with-custom-hooks
[npm]: https://www.npmjs.com/
[react-native-flex-image]: https://github.com/kodefox/react-native-flex-image
[snack-playground]: https://snack.expo.dev/@wkovacs64/responsiveimageview
[expo-go]: https://expo.dev/client
[kodefox]: https://github.com/kodefox
[mjackson]: https://github.com/mjackson
[use-a-render-prop]: https://medium.com/@mjackson/use-a-render-prop-50de598f11ce
[faccs-and-ci]: http://americanexpress.io/faccs-are-an-antipattern/
[kentcdodds]: https://github.com/kentcdodds
[kent-prop-getters]:
https://kentcdodds.com/blog/how-to-give-rendering-control-to-users-with-prop-getters
[examples-directory]:
https://github.com/wKovacs64/react-native-responsive-image-view/tree/main/examples
[license]: https://github.com/wKovacs64/react-native-responsive-image-view/tree/main/LICENSE.txt