Alpine.js is worth a look as well [1]. I've been working with it as part of the newly christened "PETAL stack" [2] of Phoenix, Elixir, Tailwind, Alpine.js, and LiveView.
> "PETAL stack" of Phoenix, Elixir, Tailwind, Alpine.js, and LiveView
Oh, finally a hipster stack after my own heart. My initial stack used Web Components instead of Alpine.js, but after one year trying to make it work (using lit-element) I'm battered, bruised and I declare Web Components a failed piece of technology [1].
Alpine is interesting, but not powerful enough for my use case, which is encapsulating common UI behaviours in reusable packages. Stimulus seem to be the missing ingredient in the mix for me. Any other suggestion for lightweight rich JS components that do not require one to buy into Vue, React or other "full-stack" JS libraries?
Also, I wholeheartedly recommend Unpoly as well, fits nicely in the PETAL stack.
1: shadow DOM isn't great on your OWN components, styling isn't fun paired with global CSS/tailwind, dealing with properties/attributes/observables/reactivity in general is an absolute pain in the buttocks, good luck wrapping an <input> in a web component.
> Any other suggestion for lightweight rich JS components that do not require one to buy into Vue, React or other "full-stack" JS libraries?
Depending on your needs, you can sort of use Alpine + server-side templating to achieve this. While Alpine itself has no concept of components, you can define a template fragment that consists solely of the “component” for basically the same effect. Because the x-data directive is freshly evaluated each time it appears, you can include a fragment multiple times and get isolated “instances”.
YMMV though, I eventually found that because templating is much more simplistic than a real tree of components as you would get with Vue or React, things started getting hard to follow, especially when my “components” were nested, or involved approximating slots.
Ultimately I converted to Inertia.js, which is a very small glue library that lets me write the whole front end in Vue (or React, or Svelte) while keeping a more traditional Laravel (or Rails) backend.
Shadow dom is optional for web components, web components are still useful without it, so I hardly see how that could be a reason for "failed piece of technology".
Also, did you try with pure JS ? no lit ? I just removed StencilJS from my component to go pure-JS and it turns out I really prefer it that way, maybe it would suit you too to do WC without any additional JS lib ?
lit-element is a very light wrapper on top of the native API. The problem isn't lit-element, the problem is the native API is not that good, not ergonomic, still incomplete and so far removed from the modern way of building reactive websites, i.e. view = render(state)
You need some abstraction on top of the native API, working directly with it is even more painful. The fact I'm using lit-element is because many WC libraries force shadom DOM upon you, and only some make it optional (Stencil is one of those few, but the TS requirement is a no-go for my use case)
Not the OP, but slots are part of the shadow DOM spec, so if you don’t want isolated styles, you have to forego slots.
I tend to build my app scaffolding with wrapper components that provide layout and/or functionality to their child components via slots, so as much as I like the idea of WebComponents they’re a complete non-starter for me.
In the above stack LiveView doesn’t play that well with web components that render their own children, as it will remove those children when it rerenders. So you do kind of need shadow DOM.
React is great if all your frontend is React. Using it to create reusable components to sprinkle in your static HTML is overkill, and anyway it does not work with LiveView and LiveView is pretty cool.
IMO React has a good data model and solid foundation, but the whole ecosystem it's relying upon is bad. I'm not a fan.
Sure, but where is your data and business logic going to live? If the answer is "just write it in JS" then it's no longer a question of "just use React" but "let's replace our entire stack with Javascript".
Ya it really depends. I like redis and Postgres generally speaking for data, and Ruby on Rails generally for business logic. And you can certainly do universal rendering with that easily with react_on_rails. If you do end up needing more perf, maybe go would be interesting.
Yes but we're still going a long way from "just use React". One benefit of the "JS sprinkles" approach of Turbolinks+Stimulus is that you have a far simpler and more productive stack: Rails (or other framework) renders templates with some minimal JS on the frontend, as opposed to the inherent complexity of API + SSR + SPA. There are occasions when React is a good choice of course, but let's not underestimate the costs of the SPA architecture.
Why would the entire stack be JS? Even in server side React, it can still fetch from APIs, so presumably you have a backend to fetch from, in whatever language and database you want.
yup, and if you want to really optimize it you could use functions as a service like lambda as your react rendering layer, which forwards to heroku or fargate or beanstalk or vanilla ec2 or whatever for your backend.
and if cloudflare workers ever support metered usage you could use that too for the react layer.
and if you want hyper performance you could use lambda@edge and intelligently route to your backend running on fly.io to minimize the distance. the somewhat unsolved problem there though is multi region master master replicated databases which are cost effective.
I thought you were making joke about complexity when I read the first line, but then in the end it's not a joke. Why don't you have 1 single backend peacefully. JS sprinkles for what it needs to be interactive. (I'm tired of "complex form " argument, jQuery turned out ok back then, so does Stimulus or whatever works with server rendered html (e.g. vue))
This sounds like madness. Not cost effective to deploy, nor cost effective at all to implement in the first place. It's literally setting money on fire.
What's the equivalent of Poe's law, but for software development?
I've also been playing with Alpine, but on the also-newly-christened "TALL stack" [1] of Tailwind, Alpine.js, Laravel, and LiveWire. It's also worth a look.
I was thinking that Alpine here was referring to Alpine Linux, which I found strange in the context because often these sorts of acronyms (LAMP, MEAN, etc etc) don’t mention specific distros and I also would not expect CSS-Tricks.com to write about Linux distros. But the answer of course was that it’s not Alpine Linux, it’s something called Alpine.js.
I worked with Stimulus for about a month and found it to be frustrating:
1. There is not a lot of documentation
2. They don't provide any testing guidelines, the best I've found is hand-wavy test-with-a-browser stuff
3. Everything is essentially global
4. Functions are disconnected from their parameters, i.e. I can't tell which bits of data a function is using without digging through a bunch of code.
5. Putting state in your HTML is tricky if you also want to modify the DOM.
6. Their naming scheme is cumbersome, e.g. data-controller="using-a--sub-directory" and data-target="some--nested--target-has-a.function", that is, the fact that everything is location-in-your-code-file-structure based.
And a lot of other small things.
Ironically, using Stimulus convinced me to switch to Vue because I liked their value proposition of "Javascript sprinkles for your HTML", which Vue lets me do, but more intuitively.
Ditto. I think if you are an experienced web app dev, understand the pros and cons of something like Stimulus, and set out from the beginning of your project to make PRODUCT decisions within the constraints of something like Stimulus and being more SSR. Then it is a great tool.
Anything more complicated though, and you are better off with Vue, React, Etc
I am an experienced web dev and went into Stimulus excited to have something that seemed straightforward, but was disappointed. Vue has the flexibility to do SSR HTML with a bit of javascript for behavior way that works well for me.
Just being able to put
<button @click.prevent="save(<%= id %>)">Save</button>
Instead of:
<button data-action="click->some--long--path-to#save" data-id="<%= id %>">Save</button>
I've personally hit the point where I feel like React is my go to for anything - especially with Gatsby.
* If I'm keeping it simple, it's barely different than pure HTML.
* Free templating
* If I need Javascript (which I likely will), it's extremely easy to add anything I need.
I like React a lot and I sometimes use it for my own projects (e.g. I just finished https://techadequiz.com/)
However, I don't think React does "add some Javascript behavior to my Rails views (or backend framework equivalent)" very well (which is what I think Stimulus is trying to do). I mean you have solid support for React in the Rails ecosystem, but you turn your HTML-rendering responsibilities over to React Javascript functions.
I haven't had a problem. In my experience, I can actually get content up more quickly than a traditional CMS-type system.
Gatsby does some intelligent bundling so each page is pretty much self-bundled.
* Root HTML file
* Core, common JS (cached after first load)
* Page specific JS (also cached on subsequent loads)
For me, this is 257 kB uncompressed - with most of that being the Root HTML file (I realized I don't have minification or Gzip on - which takes that down substantially). Not a tiny payload, but the content is all static so the response comes back extremely quickly.
Even at slow 3G speeds, I barely get a flash. The actual page loads just about as quickly as HN.
With modern internet, the load time is nearly instant AND is literally instant on subsequent page loads (since most of the payload is locally cached).
It's not. If you're building a backend heavy use-case, there are plenty of good options.
Gatsby is geared towards building web pages, not web applications. If you're implementing Gatsby, you're building essentially HTML pages, possibly with some JS functionality.
Huh? I think Knockout.js was awesome for its time! To me, Vue.js feels like Knockout’s spiritual successor. What did you find frustrating about Knockout?
I have great hopes that Stimulus 2.0 and Ruby 3.0 will put Rails back in the spotlight as the sane alternative to SPA abuse. Ruby and Rails have some really talented people on their teams and there's something special about the Ruby language which makes it such a fertile environment for innovation. Only Clojure compares in this respect.
Rails is already the sane alternative. Any full stack framework is. DotNet, Laravel, Django, Rails...
If you think you need a JS framework to handle your UI, you've already lost, and you're doomed to reinventing wheels that these technologies have already perfected.
Came to say the same. I've found it to be a really great productive combination. I can sprinkle in React components where the UI is especially rich and use Rails for everything else.
SPA abuse is the predictable and highly unimaginative result of framework overuse. Hoping a different framework will somehow be a solution is exceedingly optimistic.
Stimulus has a very small surface area, but I find it really enjoyable to use when combined with Turbolinks.
My only complaint/comment is that some of the HTML that I need to generate ends up being littered with so many data attributes, especially when combining multiple Stimulus controllers/behaviors.
Has anybody found a clean way around that in Rails templates?
Same advice as when using Tailwind CSS, or anything of this sort: refactor your application-specific front-end parts at the point of generation, into purposefully named units. For Rails, that means shifting your boilerplate into helpers, partials, and partial layouts. If you're ready for a sharper leading edge, look at ViewComponent.
Once the boundaries of your server-side parts for something are congruent to the client-side parts, you have reached the nirvana state of wondering, "can I extract & package these into a library and share it?".
This is general advice for all development but particularly and specifically true for Rails devs.
We adopted stimulusjs a couple of years ago for adding some basic interactivity on our pages and it was a decent tool, but we quickly grew out of it once we started adding more complex (form based) front-end behavior involving lots of state changes. We're currently trying to migrate a significant amount of stimulus code over to react.
Not knocking on stimulusjs, but just be wary that it does not grow well with increasing client side complexity. You'll end up writing a lot of javascript boilerplate / DOM manipulation code / custom state management components. From my experience, it's a nice lightweight tool for when you:
1. Just need to add some light interactivity (toggling visibility of components, any basic view filtering) that do not involve a lot of logic
2. Don't need j.s unit tests (stimulus has not been the most testable tool - we lean mostly on integration tests).
One typical example is a component that collects (and displays) education history provided by the user. I'll list out just a few key UI behavior:
* A "read" view which represents a list of containers with titles that show each instance of a users answer (for example, if you provided 5 sets of answers, such as 5 universities you attended, we need to show those 5 in some sort of preview form on the page)
* A button that when clicked opens up a modal which contains multiple "steps" - each step is a small set of form inputs that range from simple ones like date to more complex ones involving search / autocomplete or file uploading.
* A button inside the modal that lets you navigate through the various steps / groups of form inputs.
* A "save" button inside the modal that persists your answers. This closes the modal and shows your set of answers as an instance in the list view (see the first bullet).
* In-line validation errors as the user is filling out each step of the modal
* Messages to notify the user that a modal needs to be opened and completed (Like if the user closed the modal before filling everything out, we want to let them know it's not fully completed).
Where some of the biggest complexity crop up:
* Form input data handling and general state management. As I mentioned, we can have upwards of maybe 20 controls in this component. At minimum, we need to write code to listen to input changes and make sure this new information is kept in sync with both internal component state and other parts of the UI. If you gave us a valid answer, we need to make sure to update the UI to show a checkmark (in the simple case). We also have controls that show or hide based on your answers to other form controls, so we'll need to handle inputs by potentially hiding or showing other inputs.
* Ok, so what if we need to write some UI updating code? Well, updating any part of the UI requires manual DOM manipulation. At minimum, this can be something simple like toggling visibility of an element by adding or removing a class. At worst, this involves appending html template strings (like if we need to show a list of errors on top of the modal) when the user attempts to save bad answers.
* Cross-component communication is difficult. Want to add a subcomponent? And it needs to communicate with the parent? For example, each step in our view is sort of a "sub form" and it needs to talk to the parent "modal" to keep global state - you'll need to roll some basic event handling code between the two.
* Testability. All these DOM changes - how do we test that our component is behaving? At the time we adopted stimulus, there wasn't a standard way of testing our stimulus controller so we've mostly leaned on (expensive) UI integration tests.
Some of this complexity is sort of inherent to forms - forms are complex UI components. There are hosts of libraries in other frameworks for dealing with form inputs alone (in order to cut down on the boilerplate you have to write). For example, formik in react helps cut down on a ton of boilerplate you have to write in order to wire the form control DOM state to react state.
Some of this complexity is also our own doing - we keep A LOT of state in the DOM. If a user blanks out an answer, we need to update the DOM with an additional form control whose value will get passed back to the backend to persist the change in the DB. Moving over to ReactJS won't help us here - this will require changes to our backend vs frontend API's.
However, having to handle the bulk of those DOM changes yourself requires a lot of code, is pretty error prone, and quite hard overall to maintain (as anyone who has rolled apps using vanilla javascript can attest to). StimulusJS doesn't offer many facilities for changing your UI in a declarative way.
Finally, cross-component communication is very common and while there is a number of ways to pass data back and form between stimulus controllers, there isn't really a nice way to do it outside of passing events. This is fine for simple cases, but error prone for our use cases. In reactjs, invoking callbacks that change parent state feels much more straightforward and has been easier to test.
I don't know enough about which parts of the site you're referring to to say for sure, but stimulus / stimulus-like approaches are perfectly fine for most display-only views even if they're fairly complex.
There are pages in our app where stimulus works great and the UI is fairly complex from a standpoint of the number of strictly user facing behavior (click this, show X, Y, and Z).
The complexity involved in handling form UI is just not very suitable for stimulus from my experience. That said, you can still do a lot in terms of following good programming practices to create something that looks and works great and is reasonably maintainable.
It ultimately comes down to your (and your teams) tolerance and business needs - there's no hard reason we need to stop using stimulus, we've just decided that there are parts of our app where we could make leaps in developer productivity by using something else.
I built complex form before on financial platform where it contains complex investment details, different entities for different kind of offering .. with vanilla javascript. But that may sound grumpy old man in argument. Stimulus' weak point may be where it requires many DOM mutation per action, that's probably better use declarative way to re-render the whole thing in different way based on data.
Not at all - I still favor vanillajs if only to avoid the bloat of the whole modern js toolchain (webpack...babel...extra compile times)
There's also a big difference for us within the form world between forms with ephemeral state and forms that need to persist state through many interactions and sessions. The former is perfectly fine for us to handle with vanilla js, the latter just becomes easier for developers with a declarative model.
Thank you for the detailed answer. I can definitely see you’ve crossed the line of adding subtle interactivity to the dom :)
The behaviors you describe are intricate and contain interconnected components which are probably managed by something like vuex (or the react equivalent)
Yeah, I also want to emphasize (again) that none of this is a criticism of stimulus - the key words / phrases they used two years ago and continue to use are things like "modest" and "augmenting html". Our needs were very different then. I'm not complaining that my hand saw isn't doing the same amount of work as a chain saw.
Awesome answer. I work a lot on business systems with complex forms and agree with everything you say.
We are still feeling our way for a solution as we don't want to move to a SPA (swapping one set of costs for another) but have outgrown our current SSR + hand-rolled JS + jQuery + Bootstrap JS mess.
I still have reservations about moving too heavily towards an SPA - we still mostly have server-side rendered pages that render with react components so we don't have to deal with things like client side routing. It's also still important for us we render most of the page server-side for performance reasons, but I'm not too informed about the specific trade-offs there in load times if we did push more rendering to the client.
I feel like there's some opportunity in the space to create a truly re-usable form library that can hook into your backend and be themed / customized to suite the look and feel of your app. There's some companies already in that space though, so I might take some time to check out some existing solutions and see if there's some kind of needs gap. What's challenging about this problem though (as I'm sure you're also aware) is that there are a ton of variability in requirements when it comes to forms - it's something I wish were more standardized but at times it feels like one of those bikeshedding topics where everyone has an opinion on how inputs show be displayed, how errors are shown, etc.
> There's some companies already in that space though, so I might take some time to check out some existing solutions and see if there's some kind of needs gap.
Which companies are you thinking of? It's been too long since I looked beyond $dayjob and investigated what could be possible.
Unpoly https://unpoly.com is also worth a look if you are interested in these things. It combines Turbolinks and Stimulus functionality in a very usable way.
I’m currently dealing with a modal issue within Svelte (pretty sure it’s due to createDispatch) and the Discord moves too fast to get help.
Gonna check this out in the AM.
EDIT: Couldn't sleep so checked it out now. Looking through the issues, there are a couple that are apparently "fixed in 1.0" but it doesn't look like 1.0 is coming anytime soon. Shame.
And maybe documentation too. Turbolinks has really good docs and conceptually it's pretty easy to get going (1 line of JS and learning a few event handlers). Alpine and StimulusJS have really good docs / guides and tons of practical examples if you Google for a specific problem you're trying to solve.
Unpoly's docs seem like it's mostly an HTML front-end to their API spec with very few examples. It's kind of difficult to figure out how to use most of what it does. There's also very little practical examples of using it if you Google around.
I've heard of it in the past but never got into it because of the above.
Unpoly is pretty good and complete. What I don't like is loading page specific JS/CSS via javascript. Turbolinks manages that for me and turbolink still executes inline JS.
Turbolinks falls short in providing a "framework" in how to use third party libraries - how to initialize them and how to tear them down. I finally glued together a mini-framework with hints from stack overflow, but Unpoly and their concept of compilers[1] is much more consistent.
Yeah. This is happening with all libraries by basecamp: They release something, they don‘t work on it for months (no commits, many open pull requests) and people believe it‘s dead. Then one day (today) they out of the blue release a new version which nobody was expecting.
Wouldnt stimulus and turbolinks not work so great i would prefer using different libraries.
Htmx is more like all „we try to solve everything for you but fail“ solutions. The good part of stimulus is that it is only a binding library, no automatic content refetch etc. solution
As for NEW MAGIC, DHH mentioned it will have a Rails server side aspect to it so I don't think we can compare these tools directly.
He did say other server side frameworks could implement similar behavior too but it would involve the framework having built-in support for websockets and job queues. This is based on a question I just asked him on Twitter at https://twitter.com/dhh/status/1334953167327547397.
So it sounds like NEW MAGIC will probably make it super easy to push / broadcast stuff from the server to the client, but to get everything working with a really nice API and it being seamless will require a substantial amount of work if you planned to re-invent it in another framework.
The new turbolinks they use on hey.com is using a concept named „portals“ so different parts of the web application can be refreshed independently. My linked inside hey.com articles are very interesting.
Yeah that's the interesting part. It sounds like maybe there will be some overlaps in Turbolinks 6 and NEW MAGIC when it comes to partial updates.
Maybe for basic partial page updates where you don't need to broadcast something to every connected client over websockets then Turbolinks 6 will be enough on it own. If that ends up being the case that's amazing because it'll be fully client side.
In any case the near future is looking to be the best its ever been for developing very nice feeling apps without going the SPA route with any back-end technology.
I've been following them since the intercooler days and they've been great (functionally) although maybe a little drowned out by all the SPA chatter. Still a great library IMO.
This is good news. I work for 6 months with StimulusJS 1 and I'm very happy to use it. I use it mainly in a rails app combined with Turbolinks.
My original thought was to go for an SPA with a rails API backend. I tried out both React & Vue extensively but got stuck in a while. Since implementing Rails and plain 'vanilla' JS it went better. But with StimulusJS 1+ it went simply great.
Allready upgraded to 2.0 and it feels even more structured with the new features, like values and improved targets.
What's the difference between Pjax and Turbolinks? Just a generic version of the same thing? Could Stimulus [Reflex] be hacked to work with Django or does it have strong ties to Rails?
Stimulus doesn't care about your backend, it's pure client side JS. You just load it (Stimulus itself and your controllers) and it looks for Stimulus stuff inside your HTML.
StimulusReflex is a Rails thing that's much more involved than Stimulus.
They are both JS frameworks, but they are fundamentally different approaches.
React (and React-rails) moves rendering / state / logic clientside. You can use tools like react-rails to more easily pass data from your Rails controller to your react components (basically conveniences to create props from instance variables).
Stimulus doesn't handle rendering at all. It's intended to be used for small little "sprinkles" of JavaScript (think things like: showing / hiding content, toggling classes, basic event handlers). If you used to write jQuery snippets to wire up a click event to run ~5-10 lines of JS, then you might look at Stimulus as a more modern implementation (es6, mutationobserver, etc). Stimulus plays nicely with Turbolinks since it needs existing HTML (servered rendered from normal Rails views) to attach to.
You're basically looking at two diverging paths: do you go down a JS-driven SPA application with React (or some kind of hybrid where you have React do parts of the page) or do you opt for Basecamp's "I cant belive its not a SPA" approach with Turbolinks, server-rendered HTML, and Stimulus for small interactivity.
Or a convincing Stimulus vs. Vue post would be helpful too, since Vue is so simple to sprinkle in as well, and doesn't pollute the html with data attributes as much as Stimulus.
One benefit is when combined with Turbolinks there is a need to bootstrap the Vue/React component. The Stimulus controllers respond to mutation observers and always work.
If a new HTML element is added to the DOM and has a controller, it just works and is now active. This might not be the case with React or Vue, as you'd have to then bootstrap the component.
This makes Stimulus a really good combo when used with Turbolinks or partial Javascript responses.
Yes. It is for "JS Sprinkles" on your existing HTML. This makes it "modest" compared to something like React that would typically generate HTML/DOM for you.
The idea is you render the html as is. The data attributes take care of the interactive parts, with the js scripts loaded after the page renders (as opposed to render html within js that executes on load with, the reactjs render() call).
[1] https://css-tricks.com/alpine-js-the-javascript-framework-th...
[2] https://thinkingelixir.com/podcast-episodes/021-tailwind-css...