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 serverclient
- Holds the React bundleshared
- 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";45var server = new WebpackDevServer(webpack(config), {6 // webpack-dev-server options7 publicPath: config.output.publicPath,8 hot: true,9 stats: { colors: true },10});1112server.listen(8080, "localhost", function() {});
Create webpack.config.dev.js in the root of your folder:
1var webpack = require('webpack');23module.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();56import routes from "../shared/routes";78app.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});1415var server = app.listen(3000, function () {16 var host = server.address().address;17 var port = server.address().port;1819 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";45Router.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";34import AppHandler from "./components/AppHandler";56export 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