React Component including Fundamental Image Features

On web development using React, we often create <Image> component. That is stateless and including basic features as image 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 image by wrapping <img>.

  • Render images with object-fit and its fallback using background-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 create aria-labelattribute when alt property is specified with assuming <img> .

Further ideas: support convenient properties wrapping <picture> element 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 have 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 original image until intersected into 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☺️

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.