A microservice to screenshot React Components

A microservices-driven Proof of Concept

Eduardo Romero
4 min readMay 4, 2017

Over the last two weeks I worked with my team on a Proof of Concept of a headless WordPress implementation for a client. As part of the work, we had to develop a couple of embeddable media elements that Editors use again and again, like a Tweet, or YouTube video.

Optimizing for reusability, we planned to build our solution as React components that can be inserted into Wordpress’ Visual Editor and reused at the decoupled frontend.

We didn’t have enough time on the sprint to replace TinyMCE with SlateJS, DraftJS or React Quill. Instead, we decided to “fake it ’til we make it”.

I thought of a microservice that would return an Image of what the rendered component will look like on the frontend. We had a lot to do, so we got down to business.

Microservice all things!

Splitting the work

Our Frontend Team created a simple page that takes one of our Embeddable Components, renders it and signals the page that it is ready.

The React Component <TwitterComponent id=”857342043017891840”/> rendered on the frontend.

Our WordPress Team implemented a shortcode that inserts the image to the Visual Editor, adds it to the toolbar and includes a dialog for the component’s parameters (including the splashy visuals).

On the microservices team, we visit the component, wait until the page says it’s ready, take a screenshot, trim the empty window space, save the image to S3 for better performance, and put it on the cache.

If the component is already in cache, the browser gets redirected automagically to the S3 location giving Editors a faster experience.

Mocked up React Component as an Image from S3. Notice the 301 Redirect on the Network tab.

We embraced the Microservices architecture and reaped all its benefits:

  • Well-defined service boundaries we can work on.
  • Full ownership of one piece of the puzzle by each team.
  • Simpler services, with simple interactions and easier to maintain.
  • Independent technology stacks and deployments for each service.

And everything in time for our demo.

Microservice Implementation Details

Our microservice (μs) interacting with other Services.

1. Serving the Content 👨🏻‍🍳

Micro is a library for async http microservices. It follows a one function as a service paradigm that resembles AWS Lambda. Our service only responds to GET requests because we are only answering to the <img /> tag. The image’s source URL will include the component and any parameters it needs.

It then answers with one of two things: An image location from in-memory cache or an async process to generate the component’s image.

2. Taking the screenshot 📸

Nightmare is a high-level browser automation tool, used for end-to-end testing.

Nightmare runs Electron as “headless browser” to render our React Component. When the Frontend signals that the component is ready, it takes the screenshot and feeds it into the next step as an image buffer.

3. Trimming ✂️

The screenshot includes the drawing area of the full electron window, so we pass the image buffer to Sharp for some trimming.

4. Saving it to S3 💾

We upload the image buffer to Amazon’s S3 so we don’t have to run the whole process each time the image is requested.

5. Redirect the client 🌠

On the last step, we cache the image’s URL to memory and return a 301 Redirect to the client. Next time they request the same component, if it’s from the same browser they’ll have it locally cached from the permanent redirect. Anybody else requesting the same component will get its location from our in-memory cache.

Most request take under 700ms. The process timeouts after 5s.

This whole process takes around 700 milliseconds. Occasionally the frontend might timeout because the component takes too long to render (i.e. Twitter or YouTube didn’t respond on time). It’s set to timeout after waiting for 5 seconds with no response.

Bonus: Docker all things! 🐳

Nightmare needs an environment that can run an Electron browser. We had to setup a Node 7 Docker Image with xvfb support.

Here’s the Dockerfile and the entry point for our dockerized microservice. If you need something more specific just search for “Dockerfile Electron Headless”. There are a few images already on Docker Hub for that.

--

--

Eduardo Romero

Building software and high-performing teams at scale. Most recently, I built the platform that lets gamers play instantly on Luna, Amazon's Gaming in the cloud.