JavaScript Buzzwords Part 2: Isomorphic JavaScript and Server Side Rendering
Isomorphic is a term derived from the Greek word “isos,” which means equal, and “morph,” which means shape. In the context of JavaScript isomorphism describes code that will produce the same output in multiple environments. Isomorphic JavaScript is JavaScript code that will run the same in any environment. Isomorphism enables engineers to deliver improved user experiences across a variety of devices. It also enables something called server side rendering.
The concept of server side rendering has been around for a few years now and is used by many major companies such as Netflix and Airbnb to deliver improved perceived load times. These companies will use servers running node.js environments to render their JavaScript applications server side. The render application can then be output as a string and HTML code can be sent to the client. With server side rendering the client can instantly start to interact with the application. While this may sound insignificant. Rendering an application on the severer and sending HTML to the client has been around for as long as the world wide web; I promise you it’s more impactful than that.
In the recent years websites have become increasingly advanced. Web application architectures have fundamentally changed over the last decade. Since Jesse James Garret coined the term AJAX in 2005. Web architecture has moved to a more asynchronous model. Client code is split up from the server code. More and more of the interaction with the server is being done through JSON based REST APIs. REST APIs enable the client to send AJAX calls to the server to request data and then render it client side, allowing for more interactive experiences. With new technologies and vast improvements in JavaScript’s capabilities many JavaScript frameworks have risen to enable developers to create single page applications. Once a user is on a website it’s now possible for said user to navigate the entire site without a page reload. While these applications are great for user experience they require much more JavaScript code to run properly.
Larger JavaScript files in turn require more system resources. Clients have to now download several MBs worth of JavaScript files. Then the client has to parse and run these files. Especially with the increase in mobile phone traffic, this overhead has become significant in recent years. Mobile phones have notoriously spotty internet connection and users can be interrupted by a notification at any time. Due to short attention spans it is critical that websites load as quickly as possible.
Server side rendering is part of the solution to this. With isomorphic JavaScript the HTML output is identical regardless of the environment. This allows engineers to send the pre-rendered HTML to the client and then the client can load and parse the JavaScript files on their own time. There’s no risk of both code bases outputting different HTML, because it’s the same client code. Therefore, the client will render an identical web page and the user will never realize that the page was rendered twice.
One of the best frameworks for server side rendering is React. React-DOM comes with a renderToString method that allows you to render any react component as a string and send to the client. Despite React being isomorphic there are a few things you need to be aware of. Not all components support isomorphism and some require accommodations to be run on the server. React router requires use of different components depending on the environment the code is running in. For example I wrap my application in a BrowserHistory component when running in the browser and I use MemoryRouter on the server. If you want to learn more about React Router the documentation is great and has very thorough examples, you can check it out here.
If you’re using fetch in your application you’re going to need to switch to isomorphic-fetch. To do this simply add this import fetch in any file where you use fetch, like so
import fetch from 'isomorphic-fetch'
As far as I’ve been able to tell this maintains the same fetch behavior when run in the browser, but on the server it ignores the AJAX call to avoid raising an error.
Also you’re probably going to require babel to transpile your client code. In the client code base I’m using a lot of ES6 syntax and React JSX, this doesn’t run well on the server; in this case my server is an AWS Lambda function running node 6.10. In order to get around all the advanced JavaScript syntax I added this line to my package.json:
"scripts": {
"prepublish": "babel src --out-dir lib"
}
Assuming you have your .babelrc set up correctly, here’s an example:
{
"presets": ["es2015", "react"],
"env": {
"test": {
"plugins": ["transform-es2015-modules-commonjs"]
}
},
"plugins": [
"react-hot-loader/babel",
"babel-plugin-root-import"
]
}
The prepublish command will now transpile all the JS code from your src directory and output it in a lib directory. Inside package.json you can now change the main attribute to point to a file in the lib directory as opposed to the src directory and now you’ve got yourself a working node module that can run anywhere. Just make sure the add lib/*
to you .gitignore file and if you’re publishing your npm repository I’d recommend adding src/*
to your .npmignore file. If you want to avoid an npm repository you can add the dependency to your package.json file manually in the server code. Simply add to your server’s package.json file:
{
"dependencies": {
"client": "file:path/to/client/code/"
}
}
Now you can access your client code, by simply requiring the client package and there you have it server side rendering. It’s a little extra work, but I feel it has forced me to clean up my code and be more of aware of what components are doing what. If your looking to create a high performance web application server side rendering is quickly becoming a must.