Developing isomorphic applications using webpack

Gajus Kuizinas
HackerNoon.com
8 min readJan 9, 2017

--

tl;dr; I have set out to develop a program that enables rendering of any client-side code base server-side using webpack. The end result is https://github.com/gajus/isomorphic-webpack Proceed to “Hello, World”.

The past 6 months I have been working on a project with a requirement to serve the content on the server-side. The unnamed project is part of a website in Alexa Top 100 Global Site list. The website itself extensively utilises Edge Side Includes (ESI) to assemble the page content “at the edges of the Internet”. The requirement to serve the content server-side comes from utilising the ESI.

This was a unique challenge: I was working as part of a larger frontend team; the code will need to be maintained by the frontend team. Therefore, my focus has been to use frameworks known to frontend teams and avoid backend specific frameworks as much as possible. I have proceeded to develop the application using React and webpack.

I have had the application up and running, but there was one problem–none of the existing isomorphic-rendering solutions worked out of the box with the code base. The existing solutions required obscure configuration, ran multiple node processes (making it a pain to containerise the application), and didn’t work with all of the webpack loaders (e.g. style-loader ).

I have set out to develop one program to address all of the above. I have called it https://github.com/gajus/isomorphic-webpack. The rest of this post introduces to how it works and how to use it.

“Hello, World!”

Lets start with the “Hello, World” and build on that example.

Our example is a React application. It uses react-dom to render ReactElement and append the resulting DOM to the #app element.

/src/app/index.js

webpack is configured to use babel-loader to load the JavaScript files.

/src/webpack.config.js

A server-side script is using webpack to compile the application and express to server the contents.

/src/bin/server.js

Note: webpack-dev-middleware is not a dependency of isomorphic-webpack . Here it is used only to serve client-side application.

This isn’t an isomorphic application. Making an HTTP request simply responds with the string hard-coded in the server-side script.

An isomorphic application would evaluate the React application code and respond with the rendered application.

If you want to just checkout the code, use the following commands:

Isomorphic “Hello, World!”

What does it take to make the above application isomorphic?

The following changes need to be made to our code base:

  1. Install isomorphic-webpack
  2. Setup isomorphic-webpack using the webpack configuration
  3. Export the application as a module.
  4. Use react-dom/server renderToString to render the application.

Here is how that changes our example application:

/src/app/index.js needs to export the application:

ISOMORPHIC_WEBPACK is a constant used to differentiate between Node.js and browser environment. Presence of the constant indicates that it is a Node.js environment.

The server-side script needs to initialise the createIsomorphicWebpack compiler and use react-dom/server renderToString to render the contents of the application:

createIsomorphicWebpack overrides Node.js module resolution system, i.e. all require() calls that refer to resources that are part of the webpack bundle will be handled by isomorphic-webpack .

This made our application isomorphic. Making an HTTP request responds with the rendered React application:

Free isomorphism.

If you want to just checkout the code, use the following commands:

Using Webpack loaders

Loaders allow you to preprocess files as you require() or “load” them. [..] Loaders can transform files from a different language like, CoffeeScript to JavaScript, or inline images as data URLs. Loaders even allow you to do things like require() css files right in your JavaScript!

https://webpack.github.io/docs/loaders.html

For the purpose of this demonstration, I am going to show how to use style-loader with css-loader.

First, we need to update our webpack configuration.

/src/webpack.config.js

Note: There is nothing isomorphic-webpack specific in the above configuration. I am including the configuration only for completeness of the example.

Next, create a style sheet.

/src/app/style.css

Update the application to use the style sheet:

Finally, restart the application and make an HTTP request.

As you see, the server responds with the evaluated value of the class attribute, app-___style___greetings.

If it feels like you haven’t learned anything new in this section, then thats because there isn’t anythingisomorphic-webpack specific. There are no isomorphic-webpack specific changes to the configuration or the application. That is a truly universal code base.

You just won all the loaders!

If you want to just checkout the code, use the following commands:

Routes

This section describes an experimental implementation. I have not tested this in production. Proceed with caution. It is pretty cool, though.

react-router documentation already includes a section about server rendering. You could follow that path… (but it requires to write server-side specific code) or you could trick react-router into thinking that the script is running in a browser and avoid making any changes to your application.

By default, the createIsomorphicWebpack does not evaluate scripts in node_modules directory. This is done for performance reasons: few scripts depend on the browser environment. However, react-router (and history) do depend on browser environment.

I am going to tell createIsomorphicWebpack to evaluate react-router (and history) as if it is running in a browser. This is done using nodeExternalsWhitelist configuration.

Now react-router and history packages are included in the webpack bundle and will be executed using the faux browser environment.

However, we aren’t done yet. We need to tell what is the window URL when evaluating the code. Bundle code can be evaluated using evalCode function (evalCode is a property of the createIsomorphicWebpack result), e.g.

Now, lets make some requests:

It takes a magician to know a magician.

If you want to just checkout the code, use the following commands:

Conclusion

There are already many articles that discuss the pros and cons of server-side rendering (e.g. You’re Missing the Point of Server-Side Rendered JavaScript Apps). In my specific case, I needed server-side rendering to enable Edge Side Includes (ESI). I have achieved this by first writing the client-side application and then using isomorphic-webpack to render the application server-side.

Evaluate the pros and cons of server-side rendering and if the pros outweigh the cons, then consider using isomorphic-webpack to make your application render server-side.

Where to go next?

Hacker Noon is how hackers start their afternoons. We’re a part of the @AMI family. We are now accepting submissions and happy to discuss advertising & sponsorship opportunities.

If you enjoyed this story, we recommend reading our latest tech stories and trending tech stories. Until next time, don’t take the realities of the world for granted!

--

--

Gajus Kuizinas
HackerNoon.com

Founder, engineer interested in JavaScript, PostgreSQL and DevOps. Follow me on Twitter for outbursts about startups & engineering. https://twitter.com/kuizinas