Server-generated HTML In a Single-page Web Application

Meridian is the client framework we have built around Roadtrippers. One unique feature of Meridian is how and where we generate our HTML. Rather than rely on JavaScript to generate DOM elements for Roadtrippers, Meridian relies heavily on server-generated HTML.

The Server, You Say?

Roadtrippers is backed by a Ruby on Rails application. One thing Rails excels at is spitting out HTML. HTML generated on the server can also be cached for reuse, for example when another user visits the same blog post or place page. So while Rails can generate HTML very quickly, it can pull it from cache even faster.

That is not to say using server-generated HTML is easy to roll out. If it were easy, everyone would be doing it already. With Meridian we have built a solution that allows us to harness the speed of server-generated HTML with the responsiveness of a single-page application.

One Hundred Million Entry Points

For the purposes of this paragraph, an “entry point” will be any way a user can enter a web application. Typically, single page web apps have a single entry point. That is not the case with Roadtrippers. Roadtrippers has as many entry points as we have places in Atlas, plus however many blog posts we have, plus however many users are registered, plus..., etc. Every valid URL within Roadtrippers serves as an entry point.

We are particularly interested in those entry points that we seed into search engines. When someone searches for “POPS Soda Ranch” we want our place page to appear in the results. When the user clicks on the link, we want them to land immediately on the page for POPS Soda Ranch and have the full Roadtrippers experience.

Web Applications for Robots

Gone are the days when search engines are painstakingly curated by humans. Robots have taken those jobs, so those robots need to find our pages in order to properly index and rank them. But until recently, those robots did not care one whit about JavaScript.

Early on, before Google announced that their crawler would execute JavaScript, we needed Roadtrippers pages to appear in search results. At that time the robots relied entirely on the HTML that came during the page load.

Given the dual advantages of speed and indexing, our goal was clear: we need to generate our pages’ HTML on the server. But how do we do that and start our single-page app?

Respect the URL

Meridian uses the same route to access an object whether landing on the page as an entry point or navigating within the Roadtrippers app. The Rails server generates the HTML for both cases, based on whether the request comes from an AJAX request.

Meridian also has established conventions about where in the DOM tree views are added. These conventions make for a smooth transition between the server HTML and the client JavaScript.

Digging the Hooks In

Now that Meridian can fetch full and partial pages from the Rails app, it needs to plug that HTML into the JavaScript client. This presents a problem.

Server-rendering has become a hot topic lately among JavaScript frameworks. With React, react-router supports server generated markup, provided the server is Node.js. Ember can take advantage of server-rendered HTML using FastBoot, again, if your server is Node.js. Meridian, however, needs to work with a Rails server.

Enter Backbone

Backbone is not a sexy framework these days. It is widely regarded as the tool with which an application’s framework is built, and that has certainly been the case with Meridian. Ember and React bring their own standards of how things are to be done, but they don’t fit with how Roadtrippers functions. Backbone’s permissiveness allows Meridian to plug arbitrary HTML into a Backbone.View.

Each Backbone.View has a member called $el that wraps up the DOM generated by the render function. However, Backbone.View allows us to give it some DOM to tuck inside of $el.

With a little bit of jQuery we can give Backbone.View a DOM element:

new ShowPlaceView
  el: $('show-place-view')

When we create the Backbone.View it will take the DOM element we give it and work all of its Backbone magic, just without ever using a call to render. The events and handlers defined for the Backbone.View will get bound up just like they would have when using a render function.

Meridian utilizes the HTML generated by the Rails server according to the established convention just like this. When it comes time to open a new view, we need to get more HTML from the Rails server:

$.get '/url/route/to/html', (html) ->
  new ShowPlaceView
    el: $(html)

Meridian uses the same URL to fetch the partial HTML and give it to the Backbone.View. From there on, Meridian’s views operates like normal Backbone.Views.

No Silver Bullet

The approach Meridian uses to server generated HTML does not work in all cases. Within Meridian, there are still some components that use client-side templates to generate HTML in JavaScript. We have found this is beneficial for highly interactive components for which the server round-trip takes too long to provide an excellent experience. Fortunately this technique is not all-or-nothing; it can be applied judiciously to optimize for user experience.

How Do I Convince my Boss/Team/Spouse/Benefactor/... ?

Generating HTML on your application’s server yields faster initial page loads, results in a smaller over-all payload (because you no longer have client-side templates), and a better over-all user experience. It can be rolled out piecemeal, so that the results can be measured and the hypothesis of better user experience tested. There is no need to generate all of the HTML on the server, but for those components where it has great impact it can and should be utilized.

Software architect at Roadtrippers, living in and loving Cincinnati with his family.