HTMX and Raku Cro

This post describes how to apply HTMX functionality with the Raku Cro web framework. I have already shared a couple of precursor posts that explain separately Why HTMX? and Why Cro?.

Why HTMX and Cro together?

I seek a simple life. I write websites for a living and over the years I have become worried about the avalanche of technologies needed to make a modern site.

I remember the dawn of the Internet, when all you needed was HTML and CSS to make nice looking (albeit static) web pages. Then came JavaScript. Cool – that added some neat dynamism. We had a kind of MVC separation of concerns with, roughly speaking, Model = HTML, View = CSS and Controller = JavaScript.

Since then a multitude of frameworks and tools has arisen and, bit by bit, they have added layers to the original ideas. With each of these layers has come complexity. We are slowly burying the original architecture and increasing the cognitive load on developers. And consequently increasing the risk of errors and the cost of maintenance.

So the promise of HTMX is reset the paradigm, to return to the simpler world of HTML-first websites (bearing in mind that HTML5 and modern CSS are vastly superior and more standard than back in the ’90s). Yet HTMX brings in all of the dynamism and interactivity expected in 99% of modern web applications.

Sure if you are writing Google Maps or Facebook, then HTMX is not for you – but for 99% of typical websites it is perfect.


Here’s one of the standard HTMX examples “click-to-edit” (follow the link to try it out):

This pattern starts with a UI that shows the details of a contact. The div has a button that will get the editing UI for the contact from /contact/1/edit:

<div hx-target="this" hx-swap="outerHTML">
    <div><label>First Name</label>: Joe</div>
    <div><label>Last Name</label>: Blow</div>
    <div><label>Email</label>: [email protected]</div>
    <button hx-get="/contact/1/edit" class="btn primary">
    Click To Edit
    </button>
</div>

This returns a form that can be used to edit the contact:

<form hx-put="/contact/1" hx-target="this" hx-swap="outerHTML">
  <div>
    <label>First Name</label>
    <input type="text" name="firstName" value="Joe">
  </div>
  <div class="form-group">
    <label>Last Name</label>
    <input type="text" name="lastName" value="Blow">
  </div>
  <div class="form-group">
    <label>Email Address</label>
    <input type="email" name="email" value="[email protected]">
  </div>
  <button class="btn">Submit</button>
  <button class="btn" hx-get="/contact/1">Cancel</button>
</form>

The form issues a PUT back to /contact/1, following the usual REST-ful pattern.


So far so good. All we need now is a web server that can serve static pages and simple RESTful API calls via get and put. And that’s where Raku Cro comes in. As one of the leading web frameworks for Raku (others are available), it can easily provide the server side capabilities via its rich support for routes, templates and webservices.

If you want to code along, then check out the sister repository to this post, https://github.com/librasteve/raku-HTMX-Examples . It just take a minute or two with the Getting Started. There, that was pretty easy, right?

Much more information on Cro is available in the Cro documents https://cro.raku.org/docs. You will note that Cro has recently been transferred to the raku community so you can be sure of a helping hand and ongoing support and development. I recommend you don’t get too bogged down in the Beyond the Basics if this is your first exposure…

Server Side Cro Routes

To bring the Click to Edit demo to life on the back end, we will make a dir tree something like this (the cro stub command will make most of this for you, then go cro run) and access with a browser (default is http://localhost:20000).

.
├── Dockerfile
├── META6.json
├── README.md
├── lib
│   └── Routes.rakumod
├── service.raku
├── static
│   └── img
│       └── tokyo.png
└── templates
    ├── edit.crotmp
    └── index.crotmp

Cro comes with a powerful Routes capability, here is our click to edit routes code:

use Cro::HTTP::Router;
use Cro::WebApp::Template;

sub routes() is export {

    my $data = {
        firstName => "Joe",
        lastName  => "Blow",
        email     => "[email protected]",
    };

    route {
        template-location 'templates';

        get -> *@path {
            static 'static', @path;
        }

        get -> {
            template 'index.crotmp', $data;
        }

        get -> 'contact', Int $id  {
            template 'index.crotmp', $data;
        }

        put -> 'contact', Int $id  {

            request-body -> %fields {
                $data{$_} = %fields{$_} for $data.keys;
            }

            template 'index.crotmp', $data;
        }

        get -> 'contact', Int $id, 'edit'  {
            template 'edit.crotmp', $data;
        }
    }
}

Some explanatory notes:

  • the variable $data is a container for our model
  • the route block sets up routes for html requests such as get, put, etc
  • there is a static route for assets (eg. the static/img/ sub directory)
  • the other routes match the click to edit HTMX API targets
  • these other routes map to cro templates (more on templates below)
  • $data is passed as the initial-topic to the cro template engine
  • the put route writes back form fields to $data before sending the template response

Server Side Cro Templates

The click to edit HTMX templates can now be refactored as Cro Templates like this for index.crotmp:

<div hx-target="this" hx-swap="outerHTML">
    <div><label>First Name</label>: <.firstName></div>
    <div><label>Last Name</label>: <.lastName></div>
    <div><label>Email</label>: <.email></div>
    <button hx-get="/contact/0/edit" class="btn btn-primary">
        Click To Edit
    </button>
</div>

and this for edit.crotmp:

<form hx-put="/contact/0" hx-target="this" hx-swap="outerHTML">
    <div>
        <label>First Name</label>
        <input type="text" name="firstName" value="<.firstName>">
    </div>
    <div class="form-group">
        <label>Last Name</label>
        <input type="text" name="lastName" value="<.lastName>">
    </div>
    <div class="form-group">
        <label>Email Address</label>
        <input type="email" name="email" value="<.email>">
    </div>
    <button class="btn">Submit</button>
    <button class="btn" hx-get="/contact/0">Cancel</button>
</form>

Cro template syntax takes angle brackets with a leading . dot like this <.name> and uses it as a key to hydrate $data<name> from the initial topic.

errr – that’s it!

Conclusion

One great strength of HTMX is that it places the emphasis of website development back on the server and not in the web browser. Arguably, this is a better architecture for most websites where the back end can be written in a language that is designed for server systems, such as Python, Go or Raku. And not JavaScript or even TypeScript.

Since both PHP and Raku have their roots in perl (the grandparent of server side web languages), Raku is a great destination for PHP coders who don’t want their wings clipped by restrictive “one best way” languages.

Call To Action

If you fancy trying this for yourself, you are welcome to come to the github repo https://github.com/librasteve/raku-HTMX-Examples and lend a hand translating all the other https://htmx.org/examples/ to Raku Cro via a PR. Installation and contribution guidelines are set out in the README.md of the repo. Feel free to raise a GitHub Issue if you need help, or join us on IRC or Discord (https://raku.org/community/)…

~librasteve

4 Comments

Leave a Comment