Skip to main content
Ben Nadel at CF Summit West 2024 (Las Vegas) with: Kishore Balakrishnan
Ben Nadel at CF Summit West 2024 (Las Vegas) with: Kishore Balakrishnan

On Starting A Side-Project: Hotwire vs. Angular

By
Published in , Comments (11)

For the last few months, I've been digging into the Hotwire framework. I was initially drawn to Hotwire on its promise of allowing me to build a SPA (Single-Page Application)-like experience using an MPA (Multi-Page Application); and, to do so with less effort. After several months of creating demos and migrating this ColdFusion blog over to using Hotwire, I feel like I have a much better sense of how Turbo Drive, Turbo Streams, and Stimulus work. But, I'm not quite sure that I want to use Hotwire when I start my next side-project.

CAUTION: This blog post is very much me talking to myself, trying to figure some things out.

I'm Happy That I Updated My Blog to Use Hotwire

I used this blog as a laboratory for learning about Hotwire. Prior to this, I had no framework at all. Sure, I had jQuery, which I eventually replaced with Umbrella JS; but, there was nothing really tying everything together. As such, moving to Hotwire somewhat forced me to organize my code better.

Plus, I do love the fact that Hotwire creates a persistent process, which reduces the amount of work each subsequent navigation event has to incur. Of course, this is a "blog" where the number of pages viewed is "1" in the vast majority of cases; so, the relevance of a "subsequent navigation event" is a matter of much debate.

I'm also thankful that Hotwire encouraged me to think about progressive enhancement and creating an experience that would still work if the JavaScript failed to load. I do believe that this is a critical ingredient for any public facing site. And, it has certainly made this site more resilient.

In the end, if I were to build another public facing "content" site, I do believe that I would choose Hotwire. I also believe that I might choose it for any "simple" back-end administrative system.

Hotwire Doesn't Address Some Important Problems

I understand that a lot of the uphill battle with Hotwire - with anything new - is lack of experience. I've been building-up a completely new mental model, and it's not always obvious how things are supposed to be done. But, after a few months of experience, I'm beginning to see that Hotwire doesn't really address a certain set of problems. Here are some things that still seem fuzzy and unanswered in my mind:

  • Scoping of Names: Hotwire uses the DOM (Document Object Model) as the source of truth; and, maps DOM attributes to Stimulus Controllers. As such, every controller has to be uniquely named. As an application grows and evolves, I suspect that Controller names will have to get longer and more complex in order to remain unique. And, since controller names are included in every "action", "param", and "target" target as well, the DOM is going to get wordy!

    ASIDE: I've see this with other Dependency-Injection (DI) frameworks as well. In fact, in my AngularJS 1.x apps, I've started to prefix my Component tag-names with m{x} where x is a globally-unique counter. Example <m44-team-members> and <m97-email-recipients>. This way, I can avoid naming collisions without having to create artificially complex names.

  • Scoping of CSS: Most of the Hotwire examples on the web seem to use Tailwind CSS. And, I suspect that some of the impetus behind this is a lack of any native CSS scoping. Since there's no inherent compilation step in a Hotwire application, there's no hook that allows Hotwire to inject template modifications that enable scoping.

  • Encapsulated Interfaces: While reusing templates for rendering is a core part of the Hotwire way, this doesn't directly address the need to encapsulate complex interfaces. Ruby on Rails has some helpers for this; but, if you're not using Rails, you're on your own.

  • Layered Routing: If your application is "flat," in that a URL route only ever maps to a single page, Hotwire "just works". But, if you want to start using "auxiliary routes" to represent page state - such as making a modal window deep-linkable from anywhere in the application - there's no a great native way to do this. You can use Turbo Frames with an advance action to change the URL; but, this doesn't really address the page-refresh issue; or, the need to simply close the modal window and revert back to the previous URL.

  • Error Handling: Whether it be a 401 Unauthorized error or a 500 Server Error, handling failures in Hotwire is non-obvious. You can hook into events and override rendering; however, since Hotwire is doing all the fetching for you, there's no clear place to put this kind of logic. And, since Turbo Frames and top-level requests seem to fail in different ways, handling errors is all the more complex.

Now, again, I have to stress that I only have a few months experience with Hotwire; so, take this all with some healthy apprehension. Plus, the Basecamp team has built Basecamp and HEY, both of which are highly dynamic, interactive applications. So, if you really know what you're doing, it's clear that Hotwire gives you the tools to get it done.

Is Hotwire Really Less Work?

Part of the Hotwire messaging is that building an application is going to be less work. But, I'm not sure it actually works-out that way. I believe that, more than anything, aspects of the control-flow just live in different places. But, I don't believe that there's actually less stuff to build.

Consider a "Contact" form. With a Contact form I need:

  • A controller action to render the form.
  • The form HTML itself.
  • A controller action to receive the submission.
  • The business logic to process the submission.
  • A controller action to render the Thank you.
  • The Thank you HTML itself.

With Hotwire, that's all done server-side. Without Hotwire, some of that is server-side and some of that is client-side. Furthermore, the server-side parts are likely spread out across page-controllers and API-controllers. But, there's nothing here that I would remove when using Hotwire; nor is there anything that I would add when using a single-page application. It's the same stuff, it's just in different places.

Of course, if you're using Hotwire because you get to use the server-side technology more often, then it's not the volume of "stuff" that matters, it's the implementation of the stuff that makes the difference for you.

Hotwire's "Back Button" is Fundamentally Better

One thing that Hotwire clearly gets right is the Back Button. When you hit the back button in a Hotwire application, Turbo Drive just pulls the page out of the cache and renders it instantly. This is something that no Single-Page Application can really do quite as well (or so it seems).

Hotwire Leads to More Reusable Widgets

If I were to choose Angular for my next side-project, then all the widgets I built would end up working in an Angular-only context. Which means, if I had something special - like a "Fancy Select Box" - I couldn't use it on any View outside of the Angular app.

With Hotwire, on the other hand, since everything is based on server-side partial reuse, any widget can be used anywhere (as long as you're loading Hotwire). Well, I mean, sort of - it depends on whether that widget was built to work via progressive enhancement.

It's All About Trade-Offs

There's no clear-cut winner here. The reason I'm even writing this post is because I'm conflicted; and, the writing helps me think-through the problem. I think there are some things that Hotwire does really well; and, I think there are some points of friction. The question then becomes, are the benefits worth the drawbacks?

If I needed the site to work without JavaScript, it would be a non-issue - Hotwire all the way. Its "progressive enhancement" approach is a true winner.

But, if I'm creating a site that has decided to rely on JavaScript, are the drawbacks of Hotwire "less bad" than the drawbacks of something like Angular? Angular has added complexity, no doubt. But, it also brings a ton of value to the table. If I'm going to buy into JavaScript, then the value-add of Angular is well worth it.

Much to consider!

Reader Comments

15,811 Comments

The other relatively large drawback to using Hotwire is that it doesn't working with .cfm file extensions natively. Which means, any site that uses Hotwire will also have to use some sort of URL-rewriting that maps all .htm requests to .cfm requests. This isn't a huge deal; but, it does add another layer of complexity where I have to define route-definitions. If I use Angular, it doesn't matter what the URLs look like.

5 Comments

Thank you, Ben, for this honest article. Some answers:

Naming stimulus controllers: I wrote https://www.npmjs.com/package/vite-stimulus-initializer. The goal is to prevent spaghetti names from getting longer and longer, and it throws an exception if there are naming conflicts.

Long spaghetti strings for Stimulus properties: Stimulus has one strength: Its initialization process, specifically the connect function that runs after a Stimulus controller reaches the surface. For me, this is the only use case for Stimulus. For components that are a bit more complex, in my opinion, and for turbo rails, the only answer is: Svelte as a custom element, but without shadow dom, as explained in my tutorial, link below.

CSS Scoping / Tailwind Why do so many people use Tailwind? Tons of classes for every case!! Classes are much better organized in frontend frameworks like bootstrap or zurb foundation. The latter is my favorite because it is the slimmer one.

Layered Routin / page refresh issue check turbo_power gem, may this solve some?

Is Hotwire Really Less Work?

Have a look at https://rubygems.org/search?query=render_turbo_stream. There are so many details that I spent months searching the world wide web and finally wrote this gem. Now I am optimistic that the answer can be a resounding yes. I see the drawback of separating frontend and backend and having to separate larger test libraries.

Angular has advantages over hotwire, but the same advantages are provided by https://svelte.dev/, which integrates well with hotwired / rails, see https://dev.to/chmich/setup-inertia-and-svelte-on-rails-7-3glk.

Conclusion

Your conclusion is: "There is no clear winner here". I am not familiar with Angular, but I have a feeling about it. I would be very curious if you would take some hours of your precious time, build an app based on https://dev.to/chmich/setup-vite-svelte-inertia-stimulus-bootstrap-foundation-on-rails-7-overview-1bk1 and https://rubygems.org/gems/render_turbo_stream. May be there are solved many details you described above.

Thanks, Ben!
Greetings, Chris

15,811 Comments

@Chris,

It looks like you're really laying down a good foundation for solving problems in a Hotwire context. I'm only vaguely familiar with how Ruby / Rails work, so it's not super easy for me to follow your gem code; but, I think I get the gist of it. You're essentially removing a lot of the boiler-plate (if I understand correctly) for rendering streams. I think that makes a lot of sense. I've also been trying to do some similar things on my end using "layouts".

I think one of the big things that I struggle with in Hotwire is the tension between developer ergonomics and progressive enhancement. I don't have a great mental-model for how to keep things working without JavaScript while at the same time not driving myself crazy trying to keep things "easy" to build.

For example, you mention integrating Svelte into Hotwire with custom elements. From what I understand (which is very little about custom elements), this wouldn't work if the JavaScript bundles failed to load, right? So, if you're going to keep something working from a progressive enhancement perspective, you'd have to let these custom elements somehow fallback to a native element.

I'm sure this is possible; but, it's not simple and it's not easy to keep both worlds in your head at the same time.

I sometimes wonder how I would approach Hotwire if I didn't think about progressive enhancement at all. That might completely change the way I wire things together. At least when I'm using something like Angular, I know that progressive enhancement is not possible. So, it removes that whole conversation / distraction from the table.

How do you approach / think about progressive enhancement?

13 Comments

The wiz bang feature IMO of Hotwire is Turbo Streams.

The idea of being able to re-render islands of content on the page and deliver fresh content to the client all from the same cfml template. No need to ship json to the client and have view specific code to unpack it and update UI elements. Using such a feature in CFML would account for 90%+ of the reactivity my business apps would need.

It makes be wonder do I want to to a full implementation of Hotwire in CFML or do I just want to be inspired by the pattern. Then in my next project just lightly implement a turbo-streams like feature in native cfml?

15,811 Comments

@Peter,

That's actually a really fascinating idea! I agree that the Turbo Stream stuff is really cool. It took me a while to start to wrap my head around it, and to figure out how this kind of stuff dove-tails with the application control flow. But yeah, being able to re-render chunks of HTML is really nice. And, if look at the implementation details for how Turbo Streams work, it's really just a bunch of .innerHTML and .append() and .prepend() calls. I mean, there's a lot of infrastructure that goes around those calls; but, there's not really much "magic" to the Turbo Stream mechanics.

One thing that really kills me (about Hotwire) though is that it won't work with .cfm extensions, at least not currently. If I'm building a "content" site (like this blog), it's not really a problem to proxy everything through .htm. But, if I was building an "app", I think it would be much more frustrating having to have URL mappings.

I really really wish it would allow me allow-list file-extensions for Turbo Drive.

5 Comments

Hi pleasant guys on that page :)

Ben

Thanks for your comment.

Now I have released version 4 of render_turbo_stream and I hope that at least my gem code is easier to follow. 4 major releases in one month! Finding a consistent naming convention was not easy, but I think the groundwork is done now.

And To the original question: «Is Hotwire really a time saver?» I am now convinced that the answer is yes!

progressive enhancement. Important question. I would say that the Rails approach of reducing javascript overall is the right way. AND Svelte is a bit of the same because unlike react for example with its virtual dom or the other big SPA frameworks that rely on much heavier javascript, svelte is easier. Of course: without Javascript it wouldnt work.

developer ergonomics. Svelte: The best I know. Rails: I love it! Turbo: In a very early stage. For the latter i wrote my gem.

Svelte in Hotwire with custom elements: The initialization process is, in my opinion, absolutely stable. I see no need for a «fallback to a native element», but also no way. If you would go with react, the most used frontend lib in the world, it does nothing else and it works. The best thing i found is the way I described in my tutorial, together with turbo: The initialization step happens on the very first page load! Then the element is built on the front, waiting to work for you. If you push this element to daylight at any later time with turbo, with any parameters, there is no more initialization step needed, this is the point that makes the work easier for you as a developer and a faster experience for the user.

How do i think about progressive enhancement? Of course, without Javascript a modern application is not really usable. But all what the big SPA frameworks like Ember, Angular, Vue do is far away from the way a web application was originally meant. Although they have solved many details, search engines have problems with them. As mentioned above, I am sure that DHH, the founder of Rails, who is always concerned about staying at the roots and does not like excessive javascript, is on the right way. When you build a page with Hotwired, the first load of all elements can be possible without Javascript, but for the user you build an application that feels like SPA. This is great and from my view a milestone in web development!

@Peter What you wrote is the idea of Hotwired and its independence from Rails. I am not familiar with CFML, so I feel I cannot answer your question, but what I see at first glance: CFML brings server code together with presentation code, and on the other hand, helps to separate them. Rails has embedding languages like .html.erb or .haml (which I use). They are really handy and necessary to put logic in views. But we all try to reduce the logic in views as much as possible. Exactly that is the approach of my gem. So I feel like we are a little bit on the same path, but I cannot say how CFML and Hotwired work together.

thanks, Chris

5 Comments

@Peter

If you want to integrate Hotwired in a framework other than Rails and if you want to have a complete workflow along with a complete testing strategy:

The frontend part should be easy, just follow hotwired.

In the backend part you can read the READMEs of the gems turbo-rails and render_turbo_stream. Check what is relevant for you and solve this part in another way.

15,811 Comments

@Chris,

Yeah, I'm still struggling quite a bit with the progressive enhancement stuff. I can absolutely see a way to follow a progressive mindset with a relatively simple view (like a "normal" web site). But, it just seems like building will become increasingly complex and the app becomes increasingly robust.

Maybe I have to get over the idea that the "progressive enhancement" will have parity with the JavaScript-based version. Maybe that's the bar that's too high. I'm not sure.

One of these days, I should sign-up for Hey to see how they did it.

5 Comments

Hi Ben,

it just seems like building will become increasingly complex and the app becomes increasingly robust

Yes, in fact this exactly is the challenge. If it is true that DHH is the or one of the first which really is going in this direction, like seems a little bit from this words «Fullfilling a vision» then its clear that we are in a early state and in a early state live is not always komfortable.

I am happy now, but I know how many details I had to solve the last months. I am curious which way you will choose next time.

Best regards, Chris

Post A Comment — I'd Love To Hear From You!

Post a Comment

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel