//

ISOMORPHIC JAVASCRIPT

ISOMORPHIC JAVASCRIPT

Isomorphic JavaScript means that we get to share the same code on the client and on the server. So basically, you are rendering your application markup on the server and piping it down as complete HTML to the browser.

Why Isomorphism?

The reasoning behind an isomrophic app is that instead of bundling all your React code into a single bundle (and then using something like <script src="bundle.js"> in a bare bones HTML page), you run a Node.js server that serves the files for you.

Rendered in server the app can simply display that rendered string to the user when they first visit the page. When the user visits the page, that rendered string gets overwritten after the bundle is downloaded and then the React app runs like normal.

JavaScript driven MVCs (angular, ember, backbone, etc.) render on DOM load, this can be really slow and can make for a bad user experience. And they are not indexable by search engines.

So the Isomorphic JavaScript will:

  • Render HTML from JavaScript app on the server
  • Browser loads with full HTML and JavaScript
  • Bootstraps the app.

Benefits of Isomorphic JavaScript

  • Faster Perceived Load Times
  • Better overall user experience
  • Search Engine Indexability
  • Progressive Enhancements
  • Easier Code Maintenance

Installation

Install Node.js (atleast 0.12).

Create a directory for your project and initialise a package.json file by running:

1npm init

Now let's install the necessary packages by running the following command:

1npm install --save-dev babel babel-loader express jade react react-hot-loader react-router webpack webpack-dev-server nodemon

After the installation, create your directory structure as follows:

1mkdir src src/server src/shared src/client views
  • server - Holds the backend Node server and Webpack dev server
  • client - Holds the React bundle
  • shared - Hold your components, flux, and routes

HTML view

Create a file called index.html in the views folder, and paste the following:

1<html>
2 <head>
3 <title>Isomorphic JavaScript</title>
4 <meta charset="utf-8"/>
5 <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
6 <meta name="description" content=""/>
7 <meta name="viewport" content="width=device-width, initial-scale=1"/>
8 </head>
9 <body>
10 <div id="app">```
11 </body>
12 <script src="http://localhost:8080/js/app.js" defer="defer"></script>
13</html>

Webpack

Create a new file called webpack.js in src/server/:

1import WebpackDevServer from "webpack-dev-server";
2import webpack from "webpack";
3import config from "../../webpack.config.dev";
4
5var server = new WebpackDevServer(webpack(config), {
6 // webpack-dev-server options
7 publicPath: config.output.publicPath,
8 hot: true,
9 stats: { colors: true },
10});
11
12server.listen(8080, "localhost", function() {});

Create webpack.config.dev.js in the root of your folder:

1var webpack = require('webpack');
2
3module.exports = {
4 devtool: 'inline-source-map',
5 entry: [
6 'webpack-dev-server/client?http://localhost:8080',
7 'webpack/hot/only-dev-server',
8 './src/client/entry',
9 ],
10 output: {
11 path: __dirname + '/public/js/',
12 filename: 'app.js',
13 publicPath: 'http://localhost:8080/js/',
14 },
15 plugins: [
16 new webpack.HotModuleReplacementPlugin(),
17 new webpack.NoErrorsPlugin(),
18 ],
19 resolve: {
20 extensions: ['', '.js']
21 },
22 module: {
23 loaders: [
24 {
25 test: /\.jsx?$/,
26 loaders: ['react-hot', 'babel-loader?experimental'],
27 exclude: /node_modules/
28 }
29 ]
30 }
31}

Node.js

Create a new file in src/server called server.js:

1import express from "express";
2import React from "react";
3import Router from "react-router";
4const app = express();
5
6import routes from "../shared/routes";
7
8app.get('/*', function (req, res) {
9 Router.run(routes, req.url, Handler => {
10 let content = React.renderToString(<Handler />);
11 res.render('index', { content: content });
12 });
13});
14
15var server = app.listen(3000, function () {
16 var host = server.address().address;
17 var port = server.address().port;
18
19 console.log('Example app listening at http://%s:%s', host, port);
20});

Client

Create src/client/entry.js and import react-router:

1import React from "react";
2import Router from "react-router";
3import routes from "../shared/routes";
4
5Router.run(routes, Router.HistoryLocation, (Handler, state) => {
6 React.render(<Handler />, document.getElementById('app'));
7});

Routes

Create routes.js in /src/shared which is the link that is combining our server and client:

1import { Route } from "react-router";
2import React from "react";
3
4import AppHandler from "./components/AppHandler";
5
6export default (
7 <Route handler={ AppHandler } path="/" />
8);

Running the server

Run following command from root directory:

1npm start

This will run a watch task for Babel to handle the es6, webpack-dev-server to hot reloading, and the Node server.

Visit http://localhost:3000, and you should see "Hello App Handler". That's our Isomorphic App!

You can find the source code in Github: Isomorphic JavaScript