A lot has changed in the fifteen years that I have been developing websites. The biggest changes happened in the browser, completely restructuring how normal web services are built. Now everybody expects your website to have at least a few dynamic elements, be it your login form, your shopping cart or a contact form.

Some websites are actually web apps, which means that they can be developed completely differently and be written as browser apps that only communicate with the server to get the dynamic data. In this case, the web server is only in charge of providing the app itself and exposing some API (mostly a JSON API) the app can consume. Webmail services, task trackers, calendars etc… are good examples of this. They are not what this post is about.

Typical Websites

Most websites can’t and shouldn’t be built like web apps, since they need to be crawlable by search engines and want to be displayed as fast as possible instead of requiring the user to first download the app (that can be a few megabytes).

This is why the approach for normal websites is typically to use a web server to generate HTML sites and provide all the dynamic functionality with HTML. You would then have a normal HTML login form, which posts the data to the server, menus that are rendered differently on the server depending on if the user is logged in or not, displaying session info, etc…

Stateful Html Graph – horizontal Stateful Html Graph – vertical

In a second step the website is “modernised” by adding JavaScript to give the website a snappier feel. The login form gets replaced so the user sees a nice spinner when logging in, some items are replaced to be loaded right in the browser to avoid having to reload the whole page and so on.

This approach is typically taken because it creates crawlable pages that can even be accessed with browsers that don’t have JavaScript enabled or are too outdated to properly execute your JavaScript.

This approach is problematic for multiple reasons though:

So in order to also have an HTML only version, you actually create a worse website for 98% of your users to accommodate 2%.

This is where the concept of stateless HTML comes into play.

When I talk about stateless HTML I mean that everything representing a user state (authentication state, geographic position of the user, etc…) should not affect the HTML you render. In other words:

Every user should get the exact same HTML for the same URL, regardless of state, geographic location or time

The first thing to do is think of which parts need to be crawlable by search engines. In the case of a recipe website, the “About” page and the single “Recipe” pages would be a good example of pages that need to be discoverable when people search for them.

Everything that represents data that is not affected by who is looking at it and from where.

The user menu (login, my account, my recipes, etc…), recipes that might suit the user's taste, a contact form and everything that is user specific and dynamic are examples of content that do not need to be included in your HTML.

Instead of generating HTML for it, build this functionality on top of your other HTML pages fully with JavaScript.

A good and easy-to-understand example of this concept is this blog – colorglare. All pages are purely static (they are served by GitHub as static HTML pages). Every user and every search engine get the exact same page every time, which allows GitHub to be very efficient in serving the page. On the bottom of the page you have dynamic content though: the message board. In the case of colorglare, I chose to go with disqus so I didn’t have to implement anything. The whole message board is loaded at runtime with JavaScript and doesn’t affect the HTML at all.

So you can see that this concept is nothing new.

Advantages of stateless HTML

Apart from the things listed in the previous section – which outlined that writing stateless HTML and adding dynamic functionality with JavaScript dramatically reduces the complexity and maintainability of your webapp – a few additional advantages need to be highlighted:

Taking it to the next level

In most cases, pages are more complex than adding a message board. You often have authentication, account management, shopping carts, rating systems, etc… At this point, most developers will start implementing this functionality, and they normally start writing the HTML for it.

In this section I will try to paint the complete picture of how a fully functioning stateless HTML website would work, and what UX benefits you get from it.

Generating the HTML pages

First of all, you still need to create your typical HTML pages. I will give a very simple example of how such a page could look like:

<!DOCTYPE html>

<meta charset="utf-8">
<title>Lacebook – A website dedicated to shoelaces</title>

<link rel="stylesheet" href="style.css">

<script defer src="app.js"></script>

<nav>
  <a href="/">Home</a>
  <a href="/about">About</a>
  <a href="/laces">Laces</a>
  <div class="account-menu">
    <!-- This will be handled by JavaScript -->
  </div>
</nav>

<section id="main">
  <!-- This is where your content goes -->
  <h1>Welcome to Lacebook!</h1>

  The best online resource for shoelaces.
</section>

<footer>
  Copyright 2014
</footer>

Now, as soon as your page loads, the JavaScript (in this case app.js) gets executed and does the following things:

I think that all of those points are pretty obvious, except for the last one, which is the subject of the next chapter.

Load all content dynamically

By putting all your content in the #main section, and always serving the same HTML blocks before and after, you make it possible to dynamically load your content and simply exchange it for every page, without needing to implement another representation of your content on the server.

When your site is loaded, your JavaScript parses the document and looks for all links that point to other HTML pages on your site, for example: /laces.html, /laces/striped-laces.html, /home.html, /about.html etc…

You then attach a click event handler on those links, “disable” the default behavior (by calling event.preventDefault()) and add your own click handler.

What the implementation should do, is:

  1. Create an AJAX request to the location (eg.: /about.html).
  2. Change the URL in the browser with the history API (this way, the back and forward buttons still work in the browser).
  3. Show a loading animation that the content is now being loaded.
  4. When the content is loaded, parse it to extract the contents of #main (you can help yourself there by adding markers in your HTML) and replace the content of your current #main section with the one you just loaded.
  5. Make sure that you handle all the links in your new #main content so they will act the same and fire off any JavaScript required for the page that just loaded.

Since all your pages are static HTML files anyway, you just get this “in page loading” functionality for free. Your links are still completely valid (you can just reload the page or send the link to someone else), there is no additional maintenance of two separate versions (the one to be served as pure HTML and the one that gets loaded with AJAX), and the initial delay of showing the account menu or determining the user’s authentication state will not be reproduced since the page is not actually reloaded.

The long-lasting struggle with slow browser adoption and stale browsers (notably IE6) is also a thing of the past, allowing developers to actually use modern features without needing to implement fallbacks for all of them to support their customers.

Of course, if you think that it is imperative that even users with disabled JavaScript must be able to access the dynamic features on your page, you need to implement an `HTML` only solution as well.
Just keep in mind that they are a minuscule minority and that there are only a few websites left that still function without JavaScript.

As stated before, none of this is really new. The main purpose of this post was to provide strict guidelines on how to build your web application in order to get the best result for the least effort:

To see all of this in action, I created a proof of concept demo page. You can view the JavaScript code at GitHub.