« Smerity.com

PJAX and Flask

Recently 37signals wrote about improving the speed, both real and perceived, of their web application Basecamp Next. Of the techniques discussed, they primarily focused around PJAX, extreme caching (cache all the things!) and infinite scrolling.
Here I'll focus just on PJAX, but the rest of the article is certainly worth reading!

Assuming I know what AJAX is, WTH is PJAX?

AJAX is commonly used to dynamically reload contents on a page without having to reload the page fully. Without a full page reload the client side experience is substantially improved: smoother, faster and less data shuffled back and forth. Unfortunately AJAX can add unwanted complexity to the codebase and doesn't degrade well for users with no Javascript.

PJAX provides a number of advantages without complicating the code substantially:

  • Positive impact on the user experience (no full page reload, similar to AJAX)
  • Degrades cleanly when Javascript is disabled
  • Doesn't confuse / hide the user's interaction with the site from Google Analytics
  • Page titles and the back button work as expected

Although all these benefits come into play, the main one to consider is simply this: transmitting and redrawing a page fully when there are shared elements is a waste.

Simple PJAX Example

Imagine a fairly standard webpage layout which involves a main piece of content surrounded by a header and footer.

  <html>
    <head>
      <title>...</title>
      <script>...JS that is reloaded and reconsidered each time...</script>
      <style>...Style that is reloaded and reconsidered each time...</style>
    </head>

    <body>
      <header>...</header>

      <div id="main-content">
      </div>

      <footer>..</footer>
    </body>
  </html>

Everything before and after #main-content other than the title, is a waste (assuming the Javascript and CSS styles are shared between pages). When PJAX requests a page, all it needs to receive is a subset of above, the title and the contents of a one element you want replaced, in this case, #main-content.

  <title>...</title>
  <!-- contents of #main-content -->

For more details, I recommend reading the brief README at PJAX's Github repository.

Using PJAX in Flask

Whenever PJAX makes a request to the webserver it sets a specific header to true: X-PJAX. Flask's default template engine, Jinja2, is already supplied with contextual information about the headers through the request object, so we can simply use that to modify the template.

Note that when X-PJAX is not set in the headers, Flask/Jinja2 degrade to rendering and returning the full webpage.

  {% if "X-PJAX" not in request.headers %}
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="utf-8">
  {% endif %}

      <title>{% block title %}{% endblock %}</title>

  {% if "X-PJAX" not in request.headers %}
      {% block head %}{% endblock %}
      {% block css %}{% endblock %}
      {% block js %}{% endblock %}
    </head>
    {% block body %}
    <body>
      {% block header %}
      <header>...</header>
      {% endblock %}
      <div id="main-content">
  {% endif %}

      {% block content %}
      {% endblock %}

  {% if "X-PJAX" not in request.headers %}
      </div>
      {% block footer %}
      <footer>...</footer>
      {% endblock %}
    </body>
  </html>
  {% endif %}

If you use this as a base template (_base.html or similar), all the pages that can benefit from PJAX will. Best of all, next to zero programmer attention is required!