Display images in React application with Rails backend

25 February 2017

I’m not sure if it’s a good idea to have a React app with Rails in the backend, because a few issues arise with this combination… One of them is for example how to be able to reference images in the front-end.

In Rails all assets are precompiled when the app is deployed, and a hash is inserted in the name of the files. This has different advantages that are outlined here; but also prevents us from knowing the final path of the files.

My solution to this problem, was to create a hash with the urls of the images and pass it down to the React application, inspired by this comment.

An interesting idea was to create a DataProvider and place the hash with the images in the context of React, so that all components can access it without having to pass it down.

class DataProvider extends Component {
  static propTypes = {
    images: PropTypes.object,
    children: PropTypes.element.isRequired,
  };

  getChildContext() {
    return {
      images: this.props.images,
    };
  }

  render() {
    return Children.only(this.props.children);
  }
}

This component can be used at the Root component, to wrap the entire application, so that the images hash is passed down. Here’s an example of a compoent that access the images hash:

class SomeComponent extends Component {
  render() {
    let images = this.context.images;
  }
}
SomeComponent.contextTypes = {
  images: React.PropTypes.object,
};

One thing we can do now is create a component that renders an image, given it’s path on the server, for example:

function imagePath(url, images) {
  let path = images[url];
  if (!path) {
    throw new Error(`Image url not found: ${path}`);
  }
  return path;
}

function Image(props, { images }) {
  return <img {...props} src={ imagePath(props.src, images) } />;
}
Image.contextTypes = { images: React.PropTypes.object };
Image.propTypes = {
  src: React.PropTypes.string.isRequired,
};

So now, inserting an image in the application can be as simple as:

<Image src="images/logo.png" />