Reproducing Medium Style Progressive Image Loading for React

Recently, I have read an article by José M. Pérez about How Medium does progressive image loading, and that is what I need about the lazy loading for background images. Here I reproduce a medium style progressive loading for practice.

GitHub repo: https://github.com/evenchange4/react-progressive-bg-image

The blur effect with small image

Progressive Image Loading

I am used to posting on the Medium platform as my blog. When I read articles on Medium, it uses a blur effect to load images. There are many approaches to do the same thing, and I choose the simplest one proposed by José M. Pérez with pure CSS solution. Besides, I find out a library, React-progressive-image for progressive loading, but it do not add any blur or fade animations by default. So, I want to reproduce it with medium-like blur animations for practice as mentioned above.

There are just a few steps I need to do:

  1. Prepare a small thumbnail for the first rendering. It is also possible to inline the small image using data URIs with the BASE64 format when you are using some tools like url-loader of webpack.
  2. Use CSS properties: filter: blur and opacity as animation effects.
  3. Load the original large image using javascript new Image() at the same time.
  4. Switch the image source from small one to large one.

Reactive Programming

Managing image loading via pure javascript is possible. It could be wrapped as a promise function to be reused later.

function loadImage(src) {
return new Promise((resolve, reject) => {
const image = new Image();
image.onload = () => resolve(src);
image.onerror = err => reject(err);
image.src = src;
});
}

Rx comes to me when I think about handling asynchronous data. Recompose’s observable utilities provide a way to take benefits of reactive programming. The mapPropsStream HOC lets me separate the logic from React component. The first argument of mapPropsStream holds the concept of props$-in, props$-out.

function ownerPropsToChildProps(props$) {
const image$ = ...;
return props$.combineLatest(image$, (props, image) => ({
...props,
image,
}));
}

We can put the image loading process into the ownerPropsToChildProps. It is also a good opportunity to test the side effects in the function. With marble testing, the data flow can be visualized more clearly. The key point to test operators like delay is to replace the scheduler with TestScheduler.

const source = ownerPropsToChildProps(props$, scheduler);
const sourceMarble = 'x-----y--------|';
const resultMarble = '(12)-3(ab)-c--d|';
scheduler.expectObservable(source).toBe(resultMarble);

*Here is how I set up the assertion in Jest runner.

Styled Components

Writing actual CSS for a library is a little inconvenient for consumers since they will need to set up css-loader for the projects. Inline-style may be a good solution. Or, styled-components provides an awesome API for decorating the React components with zero configs. This is a Img component which has a image state. Just pass the props in, and it will render the look it should be.

const Img = styled.div`
height: 100%;
background-image: url(${props => props.image});
background-repeat: no-repeat;
transition: opacity 0.3s linear;
`;

Conclusion

I have not dived deep in the technology of image compressing. Alternatively, the progressive loading approach is used. It could be implemented in a simple way, but there may be more details behind it. If you want to know more about the topic, you could watch Progressive Image Rendering — José M. Pérez | Render 2017.

Practicing the reactive programming is always the most interesting thing during developing. The library fits my personal website and it works! As for the usage of production, I think it still needs more works and tests.