Did you know that you can navigate the posts by swiping left and right?
It’s a fancy way of saying that the application is one where the client and server share components and rendering can be done on the server as well as on the client.
Why do it?
The reasons commonly cited for writing isomorphic apps are
Faster initial loads
Single page app frameworks tend to start by serving an empty html document and then fetching the content to display asynchronously. This can mean the user is looking at loading indicators or blank pages in the mean time. Sometimes, as a workaround, the server will pass along the first set of data, and the app will check for this data before making the calls.
With an isomorphic app, the initial request will handle fetching all of the data needed for display, and additionally will render the markup for that data and send the whole thing down (server side rendering).
The best aspect, in my opinion is getting to use the same libraries and components on the server as on the client. It is a very alluring prospect. This can ease maintenance and testing, make it easier to onboard to team members, and provide a more consistent development experience across the project.
This point is related to the first one. Having the full content rendered on the initial request means that web crawlers will be able to index your page appropriately.
Docker allows you to encapsulate your application with its dependencies. Docker is a large topic and will be the subject of other posts, so I won’t get into it right now but it is a great way to develop and deploy applications. In this demo we’ll use a dockerfile to build our application image.
React is a library for rendering markup. One of the cool things that react can do is render markup on the server, just as well as it can on the client. The markup that is generated on the server is keyed with special markers that the client can use to pick up where the server left off.
As long as the client is rendering the same components, with same data it will be smart enough to not redraw the page, and instead will start up the library and hook into all of the events, as if it had been used from the start.
Let’s walk through the demo app that I’ve created at https://github.com/code-vicar/IsomorphicReactBase
The app is split into 3 pieces
This is the bootstrap file for the react app that will be given to the browser
This is the express server that will be doing the server side rendering.
In development the bundle is served from memory using webpack dev middleware and hot reloading middleware.
This folder contains code that is not strictly intended for the client or the server, it is used in either context. For example, the root route
The other react components, and the history service
The history service is interesting because we’re using a webpack defined global to identify if we’re on the client or the server. The server cannot use the browser history that is the default for react router. When doing the server side rendering we must use memory history.
The express server has just a single view that is rendered for all requests. The result of the react server side rendering is passed to the view as content
This is a handlebar template, notice the triple bracket notation around the content, this means it will be rendered unescaped since we’re getting back an html string from the react-dom/server.
At this point you can run
and you’ll have an isomorphic react app with hot module reloading.
The dockerfile describes how to build an image for running this react server
First it uses the official node base image
Then it downloads gosu, which will be used later to run the container as the correct user
Then it will create a directory to hold the react server. Before copying the full application in it will first start with the package.json file so that it can create a cache layer of the npm dependencies
Then the rest of the application is copied in
Here we create a user to run this container as
Choose the port that we’ll expose the server on and copy in the entrypoint.
The entrypoint script will always be the thing that executes whenever this container is run. It inspects the command that is passed to the container and if it is an npm command it runs it as the ‘scott’ user, otherwise it lets the command pass through and execute in the container environment.
Now that we have a dockerfile we can specify images to be built from it using docker-compose.
For each image that we want to describe we can create a docker compose file. For example, the docker-compose-dev.yml file
This compose file lets us say what port to map the container to in the host, as well as specify environment setting, and much more which we aren’t using in this demo app.
With this dockerfile and docker-compose file we have everything we need to ‘dockerize’ the server. If you have a docker host already, you could clone this repo to the host and run the docker compose file.
Alternatively, I’ve included a vagrantfile that will start and provision a VM with docker server and docker-compose installed. It also has provisioning scripts to build the images from the docker compose files.
Now to get a dockerized isomorphic react app it’s as simple as
Assuming vagrant is installed of course.
I hope this walkthrough has been helpful and inspires you to try out react and docker.
Setting up tests for react components and es6 code can be fairly complicated, so it wan’t covered here, but it is certainly doable, and encouraged.
Some gotchas to be careful of
React server side rendering is synchronous, so if you have asynchronous code running in the componentWillMount lifecycle event that updates the state it won’t work on the server. Also, the componentDidMount lifecycle event only gets executed on the client, not the server. https://github.com/facebook/react/issues/1739
The react transform hot module reloader does not work with the pure function syntax for defining react components. https://github.com/gaearon/babel-plugin-react-transform/issues/57
If your build requires authorization (such as private npm modules, private github, etc…) then you’re likely to run into problems while building the docker container. Build time secrets, an issue that is frustrating many docker users, is ongoing and you can follow it here https://github.com/docker/docker/issues/13490.
Feel free to leave questions in the comments below