Sprig and the Joy of HTML

9 November 2020

I love the simplicity of HTML. Take a web site from 1999 and it will still render in a modern web browser. Sure, it will look antiquated, but it will work. HTML is the backbone of the web and it is making a comeback as a language. 

Sprig playground

While JavaScript is arguably the language of the web, modern frameworks and libraries such as Tailwind CSS and Alpine JS use JavaScript in the best way possible – to hide complexity and provide an intuitive user experience. The result is one in which you sprinkle classes and/​or attributes into your HTML as needed. These tools are made even more powerful by the fact that you can still use JavaScript to configure them and extend their functionality in unlimited ways (the No.js approach can only be taken so far).

But what about asynchronous requests to the server, do we need to still write AJAX requests by hand? Developments such as the Fetch API, Axios and jQuery make it much easier to construct AJAX requests, but they still need to be written in JavaScript and they still require boilerplate:

  1. Listen for a click
  2. Send an AJAX request to the server
  3. Do something with the response

A tiny library called htmx caught my eye while I was researching tools that could help me create a reactive Twig component setup. It was at version 0.0.3 at the time, which I initially dismissed as being too experimental, but on returning to it some time later I realised that it was in fact the successor to intercooler.js, a library that had been around for several years and which already had a healthy community and history. The change in name was due to the author’s desire to distinguish it from a traditional JavaScript library (one that helps you to write JavaScript). This seemed like an ideal time to get in on the action and I began contributing to (and subsequently became a sponsor of) htmx.

The philosophy of htmx is that it extends HTML. In the words of the author, HTML is an incomplete hypertext. for the low low cost of $0, htmx can fix that”. Like Alpine JS, htmx uses HTML attributes to add functionality to an element, emphasising Locality of Behaviour and thus making code easier to read.

<button hx-get="/events" hx-target="#events">
    Show Events
</button>

<div id="events"></div>

The code above can be read as: when this button is clicked, issue an asynchronous HTTP GET request to /events and swap the response into the element with the ID event”. So just like that, we’ve implemented the three-step process above using just two HTML attributes. Htmx provides many more attributes that can be used to control listening for events, making requests to the server and swapping the resulting HTML back into the DOM.

With htmx to provide the front-end functionality of listen, fetch, swap”, I began working on a Craft CMS plugin codenamed Bauhaus, which would later be renamed to Sprig (hat tip to Andrew Welch for the suggestion). Inspired by Laravel Livewire, the idea was to create a framework in which Twig templates could re-render themselves on user-triggered events. So for example, say we had a Twig template that outputs a list of entries.

<div id="entries">
  {% for entry in craft.entries.all() %}
    <a href="{{ entry.url }}">{{ entry.title }}</a>
  {% endfor %}
</div>

We also want it to be possible to filter the list by search terms, so we add an input field and filter the entries using the search attribute.

<input sprig name="query" value="{{ query }}">

<div id="entries">
  {% for entry in craft.entries.search(query).all() %}
    <a href="{{ entry.url }}">{{ entry.title }}</a>
  {% endfor %}
</div>

By adding a sprig attribute to the input, we are saying: when this input changes, re-render this template using the value of the input field for the variable named query”. The query variable is then used for the value of the input field as well as for filtering the entries by search term.

That one sprig attribute turns this code block into a live search component that filters the entries using the query without any browser page refreshes. This is different from how htmx works in that the variables are made up of all the input, select and textarea fields in the template, and the target of the swap is the template itself.

When dealing with Twig templates in this way, we refer to them as components. This helps to differentiate them from regular templates which exist in the context of a layout or a template partial. On the other hand, a component can be rendered independently of the template that included it.

There is a mind shift to be made here. It is almost as if you were given the superpower of being able to render Twig code on the front-end. What is actually happening is that a user-triggered event initiates an AJAX request to the server, the Twig component is re-rendered on the server and the resulting HTML is swapped back into the DOM. But because this happens so quickly and so seamlessly (often within 100 milliseconds), it feels a bit like magic. Rest assured that Sprig is actually a very lightweight plugin (unlike Rome, the core of Sprig was built in a day) that for the most part provides convenience methods for integrating htmx with Craft CMS.

So what can Sprig be used for? Live search, load more, infinite scroll, filtering, pagination, dynamic content, form submissions… the list goes on, all of which happen without full page refreshes and without the need for writing a single line of JavaScript. Take a look at the Sprig Cookbook for live examples.

A more important question, perhaps, is what do you need to learn to use Sprig? The copy-paste recipes in the cookbook will give you a head start but it is going to be very helpful to you if you also understand a bit about what is going on behind the scenes. The docs go into more detail and should give you everything you need to know to get really creative with Sprig. 

Another resource that you should check out is the Sprig Playground. This is available in the control panel with the Sprig plugin installed and allows you to experiment with a Sprig component and to save multiple playgrounds.

Sprig playground

For me, the best part about Sprig is that it’s just Twig. And if you want more control over your components then Sprig lets you create PHP Component classes.

JavaScript frameworks such as Vue.js and React certainly have their use-cases, but if you want to stay in Twig land and still be able to use interactive elements in your Craft sites then there’s Sprig.