React Component including Fundamental Image Features
On web development using React, we often create <Image>
component. That is stateless and including basic features such as lazy-load, accessibility, and some others.
Today, I try to implement such <Image>
component using openfresh/super-image and openfresh/viewport-observer which are React Component libraries by FRESH! developers.
SuperImage — provide basic features for image
<SuperImage>
provides basic features for an image by wrapping <img>
.
- Render images with
object-fit
and its fallback usingbackground-size
- Provide
alt
with empty string automatically for accessibility
object-fit
is a quite convenient property, but some browsers don’t support it yet. So when you have to support browsers widely, <SuperImage>
will be a great component.
import SuperImage from 'super-image';// ...<SuperImage
src="image.jpg"
width="160"
height="90"
alt="super-image"
fit="contain"
/>
When using object-fit
with fallback, <SuperImage>
will render images on <div>
‘s background. And then, it creates aria-label
attribute when alt
property is specified with assuming <img>
.
Further ideas: support convenient properties wrapping <picture>
elements or srcset
attributes for responsive image rendering.
ViewportObserver — observe changes of a target element intersection
<ViewportObserver>
observe changes of a target element intersection using IntersectionObserver with nicer interface for React.
import ViewportObserver from 'viewport-observer';// ...<ViewportObserver
onChange={() => console.log('onChange')}
onEnter={() => console.log('onEnter')}
onLeave={() => console.log('onLeave')}>
<div>Target Element</div>
</ViewportObserver>
As you know, <ViewportObserver>
can be used for general-purpose not only for image lazy-load. This is very useful.
IntersectionObserver is supported by Chrome, Firefox, Opera, Edge now. So you probably need to polyfill it for Safari with WICG/IntersectionObserver polyfill. Because the feature may be needed especially for the Mobile Web!
Image Component base implementation
Now combine them to create <Image>
component which has fundamental features: lazy-loading, easily layout using object-fit
, alt fallback for accessibility.
DUMMY_IMAGE
is a base64 encoded string of 1px square GIF image set as default not to load an original image until intersected into a viewport.
ROOT_MARGIN
is a DOMString passed to IntersectionObserver constructor option to add bounding box to the root when calculating intersections.
// You probably might need to polyfill
import 'intersection-observer';
import 'object.assign';import React from 'react';
import PropTypes from 'prop-types';
import SuperImage from 'super-image';
import ViewportObserver from 'viewport-observer';const DUMMY_IMAGE = 'data:image/gif;base64,R0lGODlhAQABAIAAAP//////zCH5BAEHAAAALAAAAAABAAEAAAICRAEAOw==';
const ROOT_MARGIN = '200px 0';export default class Image extends React.Component {
static propTypes = {
src: PropTypes.string.isRequired,
fit: PropTypes.oneOf(['contain', 'cover']),
width: PropTypes.string,
height: PropTypes.string
}; state = {
src: DUMMY_IMAGE
}; constructor(props) {
super(props); this.onEnter = this.onEnter.bind(this);
} onEnter() {
this.setState({
src: this.props.src
});
} render() {
return (
<ViewportObserver
rootMargin={ROOT_MARGIN}
onEnter={this.onEnter}>
<SuperImage
src={this.state.src}
fit={this.props.fit}
width={this.props.width}
height={this.props.height}
/>
</ViewportObserver>
);
}
}
For more detail usage, see READMEs of each repository, openfresh/super-image, and openfresh/viewport-observer.
If you any idea about base implementation for <Image>
, feel free to comment here☺️