Alpine.js
alpinejs.dev
alpinejs.dev
For lightweight front-end tooling, nothing has unseated the ease of Preact for me.
For anything even slightly more complicated we have things like Mithril or Preact, and yeah they avoid this sort of thing for a reason.
Imagine I'm a very early stage startup so I start out with a simple app built on Laravel. All my engineers are relatively non-senior PHP folks, not great at JavaScript. So it's rendered server-side in PHP - easier to achieve enough performance, security, maintainability by leveraging the framework. Then I want to put some tiny bit of client-side interactivity. Going full SPA with Next.js would involve a lot of extra cost and complexity. jQuery and Alpine fit well into that niche, and Alpine particularly helps with reacting to state in a way that jQuery is hard.
There are times when full-blown SPA is superior, but not nearly as often as people currently think. And the toolchains get vastly simpler when you're not doing SPA too.
Alpine is just not designed with that sort of application in mind, nor should it be.
The whole fight since 2000 was to avoid doing that through minimalistic templating and naming elements semantically so you can attach the logic from outside.
The bigger question IMHO is if you actually need a SPA. If you don't then yeah the old jquery style (now alpine.js or similar frameworks are spiritual successors) is perfectly fine.
jQuery didn't scale because there was no recommend way to split it into components and managing state.
Those day Backbone.js was amazing innovation.
If jQuery came into existance today when we know what we know about components and their role in building large applications it would be perfectly suitable for many applications.
Attaching to the don wasn’t mandatory and was completely manageable.
If anything building a web app has become much heavier and longer to develop than it ever has.
I find the sweet spot for utility frameworks like AlpineJS is when used in conjunction with server-side components [1]. For example, when designing server-side Blade components, the more you can keep within the component itself (e.g. html, styles, interactivity), the easier it is to reason about and maintain the component going forward. If your x-data function becomes unwieldy, consider moving the code to a separate Javascript function that returns an object and calling that function from x-data.
Ultimately, using tools like Alpine or Tailwind is a tradeoff. My apps tends to be relatively small and only managed by myself or a small team. If you're building large, front-end heavy apps; React, Vue, Svelte, etc. are definitely a better fit.
[1] Laravel Blade Components: https://laravel.com/docs/9.x/blade#components)
38.2kB Minified, 13.5kB Minified + Gzipped, https://bundlephobia.com/package/alpinejs@3.10.5
10.4kB Minified, 4kB Minified + Gzipped, https://bundlephobia.com/package/preact@10.11.3
I’m just waiting for the other shoe to drop. It can’t just be that simple, can it? There has to be a catch.
The extra boilerplate that doesn’t bring any value to me, as a dev, when needing to update values at runtime. I find two-way binding to just be simpler.
Same thing for the components and their props.
But I also get why some wouldn’t appreciate having things getting "automagically" done for them.
Over the years I've moved further and further from the web as a result of that belief. I still do web work when needed and as a result I'm familiar with vue and angular but I actively try to avoid knowing any more about them than I absolutely need to for accomplishing the goal.
On the contrary; components! Finally reusable pieces of UI! Try doing that when everyone on a team is hell-bent on their One Storyboard to Rule Them Al. Even if you did, @IBDesignable (used to preview components in Storyboards) would just crash anyway.
Instantaneous reloading! Even when it takes a bit of time, it takes less than 3 minutes. Pretty much instantaneous when coming from Xcode.
So, yes, React too was a pleasant surprise at first. The kind you get when you take of shoes that are just a tad too small.
At the moment it's really just Material. (And that looks a bit messy on the inside)
We need a chakra or radix or?
So it's a tailwind plugin that uses apply to create new css classes for components.
I used it for more complicated components like a mobile nav with animations, and for some re-usable pieces like a subscribe form. The downside with Alpine.data is that it splits the HTML from the JS, so developing and refactoring was a bit of a pain, and caused errors because of old variables I accidentally left in the HTML (whereas Preact/TSX would give me an in-editor error).
It also didn't really mitigate the need to wire up all the pieces your data component exposes into an HTML correctly. Though, this was working inside a simple PHP-based templating engine — maybe one that supports better snippets with parameters would make that part of the experience better.
I'm afraid this doesn't match my experience.
All your code can be in a single file (or as many as you want) with window.app = function(){return{show:false}}
It's 3KB. Even less if you opt to not have any React compatibility at all.
Alpine is 13KB.
And no, `package.lock` is not enough of a solution, because you will have to update dependencies at some point. Congrats, you now have multiple moving, breaking parts in your dependency tree that throw the weirdest and unrelated errors. So you're hunting down github issues, workarounds and patches, while still not really knowing what the problem was. The bonus here is that you need to remove these workarounds at some later point because your build tools and libraries have fixed the issues, so your code breaks again with very fun error messages or just straight up opaque and weird behaviors.
At some point we have to ask why we're doing this to ourselves. It's not fun at all.
Even a basic application requires layers upon layers of dependencies. Many of them are not as mature as people like to think either.
Any notable examples? (apart from that “force everyone ESM overnight because we feel like” movement)
Basically touching a JS project that hasn't been updated monthly is incredibly anxiety inducing.
And now there are several new build tools since a relatively short while, which makes the issue even worse. They might be improvements in some dimensions but they all invent their own little world again, promising this or that. But it all sounds so familiar and I already lost trust.
Rollup has always been a straightforward path. Vite (built on rollup) is super clean. Before that even, webpack was relatively stable between versions.
If you just used Gulp / Grunt / Browserify I am a little more sympathetic, especially with Grunt / Gulp, but I always felt their limitations were obvious.
Browserify somehow managed to snatch defeat from the jaws of victory IMO
I'm just not sure where the churn is from. If you're talking about meta frameworks, like create-react-app, that churn alot and change things underneath, I get it, though with tools like that, one should always seek to stay on the happy path only, as its otherwise a recipe for disaster.
Change, sure. We've had browserify, parcel, rollup, webpack. Then the next wave hit with snowpack, esbuild, vite, lasso, turbopack etc.
There's a few that have hung around like rollup and webpack, but there's definitely a constant feel of chasing the dragon when it comes to js bundlers/builds, if not JS tooling as a whole.
I've had compatibility issues with rebar, rubygems, pip, go modules and many more.
Once you're pulling in third party dependencies and the tools that manage them - in any development environment - you open yourself up to these issues. At some point, you have to update dependencies and/or tools (for security reasons if nothing else). And at some point, either the dependencies or the tools will break in a way that has you hunting down issues and workarounds that are outside the scope of what you're actually trying to accomplish.
Is JS significantly worse in this regard? It does seem like the propensity for many small packages increases the surface area of things that can go wrong, but that doesn't seem to be a flaw in the tooling.
And yes that means that bigger surface area leads to more problems.
This is less severe if you are only working on few projects at a time that you gradually update. It still sucks and is completely unnecessary, but only in tiny bits over a spread out time period.
However if touch code from just a year ago, then you might get some of these fun, breaking changes and bugs. Even more fun if you're not familiar with the build tool, or the packaging tool, because there are a dozen of those around as well and someone decided to use X at the time because why not. And you need a specific version of those too.
The Go modules thing is similarly painful, but it's not the best example of what I describe, because there is a very clear cut change from the previous way of doing things. It sucks but there is a clear path forward. With a JS project you get the feeling that you are walking on a minefield.
But I mentioned them for the usually very vocal crowd on HN (and others) that dominate any JS orientated conversation by pissing and crying that build tools exist.
Apparently, to this crowd, the act of compiling code is perfectly OK everywhere else but a travesty to need or want to do any of that for software that runs in a browser. Weird.
However, the kinds of problems that existed in 2015 thanks to detracting tools like WebPack and Babel don't really exist now with the fact that browsers support ES modules and Node is dragging its heels but now mostly supports ES modules.
With things like typescript and vite I now never have the problems that used to be common. In fact when I see people creating brand new projects in 2023 using WebPack I cry a little in side.
At some point having a bad tooling experience can't be solely blamed on the tools. It's a choice.
It works well. You end up with HTML like:
<div data-controller=“component” data-name-component-value”example” data-props-component-value=“{a: 1}”></div>
Until that I believe this argument is equivalent to “what about Mars”. By supporting dead slow connections on Earth you simply support them to remain so.
Literally none...this is just the argument of contrarians or people who've only ever worked on personal projects who take offense to real applications used by actual audiences.
Inflating the document size does impact paint time, but it scarcely matters here.
You don't embed same piece of code on every page request but instead you make the code cached on first download.
But honestly, at this point function-style JSX/TSX is hard to beat in terms of API. There's basically no DSL to learn (`x-for`, `x-if` and so on), you just use plain old JS with all of its existing capabilities. And it's just a JS function, in come props, out comes HTML. You technically don't even need JSX to do this, it's just syntactic sugar over plain JS function calls.
Sure, React is bloated, its full API has huge surface area (that you almost never need), and there are some icky parts (state, side effects, basically the whole hook system is ehh and doesn't fit the functional paradigm).
There's definitely room for improvement there, but I think it will probably not come from React, but some other JSX-based framework (Preact and SolidJS suffer from some of the same issues).
In any case JSX is going to be super hard to beat IMO.
The only ones I can think of are className instead of class (unfortunately a reserved word in JS), and the addition of convenient event handler attributes (like onClick), which aren’t real attributes in HTML (where handlers are attached to elements manually via JS). Other than that, it’s just like HTML, no?
(Now that I think about it, I’m not even sure that those are parts of JSX and not the React API)
Regarding being XML-like, it’s a matter of personal preference I suppose, but I prefer it that way since it’s basically HTML with tags that you can make yourself.
The biggest differences between JSX and HTML5:
1. In JSX, all elements need to be explicitly closed whereas in HTML5 elements like img and br should not be closed.
2. In JSX, attribute values are quoted strings, JS expressions in curly braces, or nested JSX expressions. In HTML5, you can leave e.g. numbers unquoted.
3. In JSX, you can use the 242 character entity references from HTML4 but not the 2231 from HTML5.
A topic not mentioned in the JSX spec is how whitespace between tags is handled, but HTML5 errs on the side of including too much whitespace in the output whereas a React app sometimes includes too little.
Everyone likes to complain about the DSL that Vue or Angular use, but by the time you learn all the exceptions and oddities of JSX you could have easily learned how to use Vue's templating system.
And the fact that it's common for someone to have no idea whether a feature is part of the JSX syntax, or the framework they're using, or the HTML spec itself is problematic. It's very clear where the v- directives come from.
[1] https://github.com/cheatcode/joystick#writing-a-component
People are often really chill and kind about answering questions and love to help people who take a genuine interest in their work!
We already have JS and HTML native to every browser with all the capabilities we need.
React/Solid/Preact and others build off this.
Alpine, Vue, Angular and company do not (x-for, v-for, ngFor etc)
I have no desire to learn a DSL specific to a single library.
It’s not that it’s complicated, you just use one or the other. But it’s very indicative of what’s going on under the hood: despite looking exactly like HTML, JSX isn’t creating HTML. It’s creating elements via JavaScript APIs.
But to the point that JSX is a DSL, that limitation is specifically because React itself is very tightly coupled to DOM semantics… but JSX explicitly has no built in semantics[1].
1: First sentence of https://facebook.github.io/jsx/ - “JSX is an XML-like syntax extension to ECMAScript without any defined semantics.”
https://developer.mozilla.org/en-US/docs/Web/API/Element/cla...
The only "debatable" thing that React does is to use the names from the JS API in JSX (where some people would expect names from the HTML API because JSX looks like HTML)
Also I had to laugh at the sibling replies:
> Narrative violation: className and friends are NOT a React thing!
> FWIW, the className prop is a React thing not a JSX thing
Don’t try to say it doesn’t cause confusion!
Let that sink in.
<Async></Async>
Other times not so much.
React does not require JSX, so it's a bit strange to raise the same issue with JSX as with the DSLs of Vue, etc.
If one wants to avoid JSX, it's always possible to write pure JS:
return (<h1 className="sc">Hello {title}</h1>);
return React.createElement('h1', {className: "sc"}, `Hello ${title}`);But JSX is a standard way to work with React. And Vue templates are a standard way to work with Vue.
JSX is a shorthand for writing those function calls. <h1> becomes React.createElement("h1"). Sure you are still largely using HTML element names, but that's only because people don't really use JSX for anything else. The whole point is that it's easier to write JSX than the underlying function calls.
You can use JSX to target things other than DOM by supplying your own function instead of React.createElement(). Here's an example of an express web-server using JSX: https://betterprogramming.pub/how-to-create-a-web-server-wit...
The first parameter is the component, which is string containing the HTML tag name for built-in components which are basically just HTML elements, such as div, span, img, h1, etc, but the actual component (a function or a class) for any other type of component. The second parameter is the props. The third parameter is the children, which happens to be a string in this case because the content of the h1 in the example is pure text, but if you'd want anything more complicated as children, you couldn't put in a string and pass that, you'd provide it more React.createElement -provided values.
Things like `htmlFor` or `className` should not be confusing - these are the official DOM properties. `style` in its DOM API also has an object-oriented API and not a string. If you are confused by these things (className vs class, ...) then potentially you started learning JSX from a completely misleading / wrong point.
I know most articles on React / JSX get that wrong, but this non-sense has to stop. After all, you do not write React in the browser because you want to generate HTML - you do write it to manipulate the DOM. On the server, you may want to generate HTML and then this can be misleading (true), but this has not been the main objective.
Most people learn JSX when they learn React. It was created by Facebook for React, after all. What does the React docs teach about className?
https://reactjs.org/docs/introducing-jsx.html
Here in the intro it doesn't even explain that you need to use className instead of class. It uses it with no explanation. Then it throws in a line about camelCasing and assumes that you understand why it changed.
Surely the JSX in depth page explains it.
https://reactjs.org/docs/jsx-in-depth.html
It does even less.
https://reactjs.org/docs/dom-elements.html#classname
Finally! In a page that seemingly has nothing to do with JSX we get told to change how we write HTML, but we're not even told why. (The htmlFor section tells us.) No wonder people are confused.
Yes, className is a property of the Element interface but it is not the attribute used when writing HTML. You're still changing how you write HTML. It is no longer standard. That is the point people are making here - JSX introduces enough edge cases that you must learn that it adds equally as much mental overhead as the template DSL for Alpine or Vue. Even if people understand the reasoning and it is not confusing, it's still a shift.
Again, because `className` is not React specific, it's JS DOM API specific. Sounds like the real problem is that people start learning React without fully learning the JS DOM API itself, then they get confused when things like `className` or `htmlFor` show up.
> Yes, className is a property of the Element interface but it is not the attribute used when writing HTML. You're still changing how you write HTML.
You're not writing HTML. That's the entire gist of the parent comment. It looks like HTML only insofar as HTML is a type of XML. But what you're really doing underneath is something like:
const el = document.getElementById('item');
el.className = el.className === 'active' ? 'inactive' : 'active';
JSX is simply syntactic sugar on top of DOM operations like that (albeit within the context of a renderer like React; Svelte eschews a renderer entirely and truly is creating DOM operations like above). In React, JSX would be direct syntactic sugar for: React.createElement('div', null, `Hello ${this.props.toWhat}`);
> It is no longer standardIt is standard, it is in the Element API, I don't know how it can get more standard than that. Again, it sounds like the problem is that people really should learn JS and DOM separately before ever touching React. Sadly, too many beginners in web dev and programming in general gravitate towards a React/Node stack, which are good production tools, but they really ought to know how we got there.
Now why I don't use DSLs is because they don't use the standard of the JS DOM API, they use their entirely new creation, like v-for or #if :else. That is why I consider Vue or Svelte templates to be DSLs while not JSX, because the latter is fully compliant with the Element spec, and it uses plain JS/TS, unless you define a DSL to be so broad as to be literally any transformation of code, which, well, I can't practically agree with.
> JSX is a DSL that looks almost like normal HTML, but differs in small uncanny-valley ways.
My point was not that its a common misconception that JSX has something to do with HTML - therefore comparing it to HTML and then being surprised that it isn't, shouldn't come as a surprise.
Also it does not differ in small uncanny-valley ways. Just one example on the syntax level: HTML has real self-closing elements (e.g., `<img>`) while JSX requires you to explicitly self-close (e.g., `<img />`). In HTML you also cannot explicitly self-close any element (e.g., `<div />` will just be parsed like `<div>`), while in JSX you can. I don't even want to start about whitespace etc. - those things are actually found / listed in any decent JSX tutorial.
But frameworks are so complex nowadays! Learning something the size of React, whatever templating language it uses is the least of my worries. Especially because calling it a DSL is a bit of an overstatement, it's always just HTML with a few custom tags/attributes that let you inject values, use loops and conditionals.
I don’t need or want to work in an environment that reinvents how to map over a list.
Frameworks and libraries have their own APIs like reacts hooks or solids signals. Yes these can be involved but they are different from the generalized solutions that the framework reinvents with the custom properties in alpine or the looping I mention
An API exposing the capabilities of a framework is different than reinventing data attributes, looping etc
Mixing HTML with JS for dynamic rendering is as controversial as using DSL.
JSX is wheels and engine attached to a horse. It's nice to have a faster horse, but it doesn't mean cars are a bad idea.
I'm using Alpine.js in a Django project and it does what it says on the tin. It blends well with the MVT style of Django along with HTMX and makes the client side interactions really easy. This is now my favorite JS framework for doing light client side JS magic.
> 15 attributes, 6 properties, and 2 methods.
But when you check the documentation you get:
> 18 "directives", 9 "magics", and 3 "globals".
Not only are the names used in the documentation different from what the main page says, but not a single one of the numbers match. Not sure what to make of that.
Its slightly misleading, but not really an outright lie. The extra items don't look at first glace like they are adding a ton of complexity or anything. Some of the unmarketed ones do look like they are more advanced features that are not needed most of the time.
[0]: https://htmx.org
Say you have a collapsible menu (Burger etc.) which you just implement with a simple CSS based toggle. You open the menu and then navigate to a different page (via htmx). All good. But now your back button will break because it will send you to the previous page but with the menu open.
In this particular case it's _kind of fine_ depending on your navigation concept and design. It might even be a feature! But this behavior just proliferates little workarounds and caveats and can lead to surprising UI bugs, especially if you need some state management in-memory etc.
So htmx is great the less JS you use on top of it, except you are very aware of how it operates and how you interact properly with it's events. It's a very well designed and small library overall - with caveats.
It is not abandoned, but rather it is considered "done" because the scope is well defined. I don't think it needs more features (as that would defeat the purpose of being lean and minimal). If you find yourself needing more than what petite-vue provides, you can either go up to Vue proper, or try https://alpinejs.dev/.
That said, I should update the README to indicate this more clearly.”
Github discussion: https://github.com/vuejs/petite-vue/discussions/53
And I wouldn't even say "tight CSP" as much as "standard CSP." To make alpine.js play nice with CSP, you have to allow unsafe-eval, which severely weakens your protection against XSS.
alpine.js claims to have a compatibility build with CSP, but it's not officially available, doesn't fully work, and the parts that are broken in the CSP build aren't documented.[0]
htmx works under CSP, but it opens a new vector for attackers to inject JS into your page, which effectively neuters CSP.[1]
I've heard good things about mithril, but I've never tried it.
> htmx allows you to define logic directly in your DOM. [...] One concern with this approach, however, is security. This is especially the case if you are injecting user-created content into your site without any sort of HTML escaping discipline.
You should, of course, escape all 3rd party untrusted content that is injected into your site to prevent, among other issues, XSS attacks. Attributes starting with hx- and data-hx, as well as inline <script> tags should be filtered.
That's been SOP for web development for aeons. I'd say the rewards vastly outweigh the costs. Not a deal breaker for me.
You should always sanitize inputs and encode outputs, but it's difficult to get that right 100% of the time. With CSP, you have a pretty robust safety net against XSS for when you encode user-controlled data incorrectly. With htmx, you're forfeiting the safety net.
I understand that not everyone wants to use CSP because a lot of libraries break it, but to me, giving up CSP is a pretty big sacrifice.
That been said, that's not how XSS has typically been handled. Usually, those encoding/escaping steps are already made part of the tight Request/Response stack in the backend (e.g. as middlewares, as part of a data mapper library, or as a filter activated on the template engine or Response library). Also, this is usually a default behavior that often requires to be explicitly disabled. And as long as you don't have some "rogue" I/O processes completely outside of that pipeline, you're set.
Because of this typical setup, I tend to consider CSP a mere redundancy for XSS. If you lose that extra protection due to htmx, what you gain seems far more valuable to me.
Very true, but it seems from my reading of the docs that standard escaping mechanisms are not enough. i.e. you also need htmx-specific ones.
> Attributes starting with hx- and data-hx ... should be filtered.
IMO, Libraries like these should state their CSP compatability upfront, Any JavaScript minimalist who are likely the target developers for these libs likely has full CSP implementation on their back-end.
Telling our library isn't compatable with CSP in a footnote seems disingenuous.
Edit: not used their Alpine or Alpine-CSP, just commenting on the example syntax in the link shared by GP.
(I don't mean learning JS/TS syntax, more the general paradigm for user-facing code which does something meaningful besides printing "hello world")
Devs are paid quite a bit of money to play russian roulette with their tool choices.
Frameworks are an abstraction and not really key to understanding anything. And devs need to understand, at least conceptually what the framework is doing (or attempting to do) without too much magical BS. Ideally that starts with working with the dom and seeing first-hand the pains and joys of adding and removing elements, functions, sync and async behaviours, data handling, objects etc.
Then only will frameworks click and make sense, and so choose the right tool for the right job.
This strongly aligns with how I like to learn, and is one of the reasons (besides lack of need) why I haven't touched JS thus far; everything seems to focus on the revolving door of frameworks, but it isn't clear as an outsider if any of them are "purer" than the others. The reason for asking about this one in particular is that at a glance, it appears minimal and clean - but I suppose that doesn't necessarily correspond to it being an idiomatic example of simple JS done well.
You'll also have better tooling to help you.
If you have any questions, feel free to get in touch: ryan.glover@cheatcode.co.
[1] https://github.com/cheatcode/joystick#writing-a-component
I’ve used it fo years. It never breaks and I don’t need to recompile never
This is an HTML framework implemented in JS, the same way numpy is a Python framework implemented in C.
For example, if someone binds an event listener to an element in two different places with something like clicker="button" in one place and then uses data-clickable="true" in another place, it can introduce confusion. Not only does the original developer have to remember all of their custom attributes but so does anybody else who comes on to the project.
[0]: https://haml.info/
One thing to note, on my mobile browser (Firefox Nightly) the site is a bit broken. The page has the wrong width so periods etc are off screen and for the code example blocks I can't scroll them to see the whole line.
There are so many Reacts devs out there that now blogs and landing pages are fully rendered client-side. With a backend only used for its API. Instead of simply sending HTML.
Alpine is great for the simple dropdowns, navigation drawers, etc. The things we used jQuery for before we started to do everything in JS.
I still love writing SPAs but I'll stick to boring HTML with JS sparkled whenever I can.
- Astro: https://astro.build/
- Try Alpine in the browser: https://astro.new/framework-alpine?on=stackblitz
Very easy to use too!
Alpine uses the dom so it’s perfect for public sites with anonymous users with mostly static content.
Makes google happy.
It’s not meant for GUIs.
At first, I (incorrectly) thought you were saying that the alpine templates are stored "in the dom", so then google would parse them. For example:
<div> Copyright ©
<span x-text="new Date().getFullYear()"></span>
</div>But, once I figured out you were talking about progressive enhancement, I came back to reality that google will only see "Copyright (C) " and won't see "2023".
Note: I think it can be used for CRUD based GUIs, if paired with "Html over the wire" (rails hotwire, phoenix liveview, laravel livewire), but that's a different point.
[0]: https://searchengineland.com/tested-googlebot-crawls-javascr...
You forgot the rest of the headline.
but I like svelte a lot :3 prefect fit for my projects for past 5 years https://github.com/kokizzu/svelte-mpa
It's an optional purchase for people who might want to rather save time, e.g. when you're a small dev team trying to build something, you need to calculate the opportunity costs.
Furthermore, sometimes I will even refuse to use a service if they don't charge money. The interests of a service will often be aligned to those who give the money. So if the money is not coming from me, the supposed customer, it has to come from someone else.
I think it’s a great way to monetise an open source project.
That's a strange mindset *pondering*
If you are a developer or a company using open source buying components and even training courses is like printing money.
It's an investment to save time and level up your skills and put out better software in less time with more polish.
Have you checked out TailwindUI. It literally replaced having a designer at our startup. Amazing value.
If you're not paying for tools you're leaving money on the table.
Plus supporting open source means the maintainer can make improvements that wouldn't be possible otherwise.
Support, Learn, Save Time, Profit!