Ride The Streetcar…All Aboard!
Oh what fun! Clang clang! Ding ding! Welcome aboard, one and all!
Ride The Streetcar 🚋, as part of the Heartml family of vanilla-adjacent web tools, is a new set of client and (optional) server libraries which provide you with a modular & resuable method of driving surgical state changes to your frontend DOM & JavaScript objects.
Ruby & JavaScript backends are supported out of the gate, and Streetcar is fully compatible with fetch style techniques as well as htmx via a custom extension. And the Streetcar frontend itself is tiny! Only 2kB minzipped with zero dependencies!
Conceptually, Streetcar is a way of describing actions to be executed on a page using custom elements. You can read a Streetcar payload containing serialized HTML-based statements…aka “streetcar line”…and ideally you’ll understand the operations being performed and in what order. Here’s a basic example:
<sc-line>
<sc-query-selector selectors="#progress-message">
<sc-inner-html>
<template>
<em>Yay!</em> Your process is complete. <button>Done</button>
</template>
</sc-inner-html>
</sc-query-selector>
</sc-line>
This will select the element in the DOM with id="progress-message" and set its inner HTML to the Yay! markup provided.
These custom elements aren’t “web components” in the traditional sense. They are non-visual and self-executing in order to translate the serialized logic into JavaScript statements when processed by the client.
You may be familiar with “pull” based hypermedia tools like htmx (logic of what to do with HTML fragments is defined client-side in HTML attributes) or “push” based tools like Hotwired Turbo (logic is defined via server-side HTML actions which the client will interpret). Streetcar is a push-based tool but happy to coordinate with existing pull-based tools—it ships with an htmx extension, and we’re more than willing to help support any other tools in this genre.
As mentioned above, you can write Streetcar HTML responses using our Ruby or JavaScript helpers:
streetcar = RideTheStreetcar.dsl
response = streetcar.ride do
query_selector("#progress-message").inner_html =
"<em>Yay!</em> Your process is complete. <button>Done</button>"
end
const streetcar = RideTheStreetcar.dsl()
const response = streetcar.ride(sc => {
sc.querySelector("#progress-message").innerHTML =
"<em>Yay!</em> Your process is complete. <button>Done</button>"
})
Streetcar is intended to play nicely with any server capable of sending HTML fragments over the wire…which is virtually any and all of them. We show a few examples of how to use Streetcar with popular Ruby and JavaScript frameworks.
✅ Framework-agnostic.
✅ Frontend-agnostic.
✅ Fully extensible to drive custom logic.
✅ Proven techniques based on decades of prior art.
And maintained by a trusted voice in open source application & framework development.
What’s not to love? 😊
The basic idea for Streetcar emerged from a blog post I wrote some while back, Action Web Components—a pattern inspired by a number of past iterations on the “push” paradigm. (Read a brief history here to learn more.)
I’m excited to discover how you may use Ride The Streetcar! To engage with the Heartml community, join the Human Web Collective Discord where you can meet friendly fellow developers who love investigating and using “vanilla-adjacent” frontend APIs and ask questions about this project and beyond.
Interested in contributing to Ride The Streetcar? Bug reports, feature requests, and PRs most welcome.