JavaScript Sprinkles with AlpineJS
<p>I’m a big of <a href="https://reactjs.org">ReactJS</a>, but sometimes it is way too heavy for what I need. The majority of the applications I create are server-rendered and just need some “sprinkles” of JavaScript. In the past, I’ve leaned on <a href="https://jquery.com">jQuery</a> for this, but I’m not a fan of the imperative nature of it.</p> <p>Recently, I stumbled upon AlpineJS, which is a super minimalist framework that lets you write declarative sprinkles of JavaScript without resorting to something heavier like React or <a href="https://vuejs.org">Vue</a>. It comes in at just 6.4kb (compressed), less than a quarter of the size of Vue, React, or jQuery. And everything is done inline within your HTML, so, like React, your logic is co-located with your markup. Unlike React and Vue, AlpineJS does not use a virtual DOM and relies and the actual DOM.</p> <p>So for a dead simple example, let’s say you wanted a button that toggled a piece of content. You use the <code>x-data</code> attribute to declare your component scope and initial data, <code>x-show</code> to toggle your content, and <code>x-on:click</code> to add the click behavior.</p> <div class="highlight"><pre class="highlight html"><code><span class="nt"><div</span> <span class="na">x-data=</span><span class="s">"{isOpen: false}"</span><span class="nt">></span> <span class="nt"><button</span> <span class="na">x-on:click=</span><span class="s">"isOpen = true"</span><span class="nt">></span>Show Content<span class="nt"></button></span> <span class="nt"><p</span> <span class="na">x-show=</span><span class="s">"isOpen"</span><span class="nt">></span>Content goes here.<span class="nt"></p></span> <span class="nt"></div></span> </code></pre></div> <p>That’s it! Simple and easy to follow. If we wanted to add a little pizazz, we could use <code>x-show.transition</code> instead and this will animate our content when it’s shown and hidden.</p> <p>My only real complaint with this, which is the same complaint I have about Angular (1.x) and Vue, is the placement of logic inside strings. The programming purist in me is not a fan of this, but I know it makes sense from a practical standpoint given the historical baggage of HTML.</p> <p>Let’s move on to a slightly more complex example. Here we’ll keep the value of a text input in sync with the text in a <code><p></code> tag. The <code>x-model</code> attribute is used for input 2-way binding and <code>x-text</code> is used for outputting the variable.</p> <div class="highlight"><pre class="highlight html"><code><span class="nt"><div</span> <span class="na">x-data=</span><span class="s">"{fullName: ''}"</span><span class="nt">></span> <span class="nt"><label></span>Full Name<span class="nt"></label></span> <span class="nt"><input</span> <span class="na">x-model=</span><span class="s">"fullName"</span> <span class="nt">/></span> <span class="nt"><p</span> <span class="na">x-text=</span><span class="s">'fullName'</span><span class="nt">></p></span> <span class="nt"></div></span> </code></pre></div> <p>But let’s say we want to prefix the name with “My name is” and only show it when the name has been filled in. We’ll use the <code>x-if</code> attribute on a <code><template></code> tag.</p> <div class="highlight"><pre class="highlight html"><code><span class="nt"><div</span> <span class="na">x-data=</span><span class="s">"{fullName: ''}"</span><span class="nt">></span> <span class="nt"><label></span>Full Name<span class="nt"></label></span> <span class="nt"><input</span> <span class="na">x-model=</span><span class="s">"fullName"</span> <span class="nt">/></span> <span class="nt"><template</span> <span class="na">x-if=</span><span class="s">"fullName.length > 0"</span><span class="nt">></span> <span class="nt"><p></span>My name is <span class="nt"><span</span> <span class="na">x-text=</span><span class="s">'fullName'</span><span class="nt">></span></p></span> <span class="nt"></template></span> <span class="nt"></div></span> </code></pre></div> <p>Lastly, let’s do about as complex of an example as I’d recommend with Alpine. We’ll loop over an array of users and display them in a table along with a form to add another user.</p> <div class="highlight"><pre class="highlight html"><code><span class="nt"><div</span> <span class="na">x-data=</span><span class="s">"{newUser: { name: '', age: 10 }, users: [{name: 'John', age: 30}, {name: 'Mary', age: 40}]}"</span><span class="nt">></span> <span class="nt"><table></span> <span class="nt"><thead></span> <span class="nt"><tr></span> <span class="nt"><th></span>Name<span class="nt"></th></span> <span class="nt"><th></span>Age<span class="nt"></th></span> <span class="nt"></tr></span> <span class="nt"></thead></span> <span class="nt"><tbody></span> <span class="nt"><template</span> <span class="na">x-for=</span><span class="s">"user in users"</span> <span class="na">:key=</span><span class="s">"users"</span><span class="nt">></span> <span class="nt"><tr></span> <span class="nt"><td</span> <span class="na">x-text=</span><span class="s">"user.name"</span> <span class="nt">/></span> <span class="nt"><td</span> <span class="na">x-text=</span><span class="s">"user.age"</span> <span class="nt">/></span> <span class="nt"></tr></span> <span class="nt"></template></span> <span class="nt"></tbody></span> <span class="nt"></table></span> <span class="nt"><div></span> <span class="nt"><p></span> <span class="nt"><label</span> <span class="na">for=</span><span class="s">'name'</span><span class="nt">></span>Name<span class="nt"></label></span> <span class="nt"><input</span> <span class="na">id=</span><span class="s">'name'</span> <span class="na">type=</span><span class="s">'text'</span> <span class="na">x-model=</span><span class="s">'newUser.name'</span> <span class="nt">/></span> <span class="nt"></p></span> <span class="nt"><p></span> <span class="nt"><label</span> <span class="na">for=</span><span class="s">'age'</span><span class="nt">></span>Age<span class="nt"></label></span> <span class="nt"><select</span> <span class="na">id=</span><span class="s">'age'</span> <span class="na">x-model=</span><span class="s">'newUser.age'</span><span class="nt">></span> <span class="nt"><option</span> <span class="na">value=</span><span class="s">'10'</span><span class="nt">></span>10<span class="nt"></option></span> <span class="nt"><option</span> <span class="na">value=</span><span class="s">'20'</span><span class="nt">></span>20<span class="nt"></option></span> <span class="nt"><option</span> <span class="na">value=</span><span class="s">'30'</span><span class="nt">></span>30<span class="nt"></option></span> <span class="nt"><option</span> <span class="na">value=</span><span class="s">'40'</span><span class="nt">></span>40<span class="nt"></option></span> <span class="nt"><option</span> <span class="na">value=</span><span class="s">'50'</span><span class="nt">></span>50<span class="nt"></option></span> <span class="nt"><option</span> <span class="na">value=</span><span class="s">'60'</span><span class="nt">></span>60<span class="nt"></option></span> <span class="nt"><option</span> <span class="na">value=</span><span class="s">'70'</span><span class="nt">></span>70<span class="nt"></option></span> <span class="nt"><option</span> <span class="na">value=</span><span class="s">'80'</span><span class="nt">></span>80<span class="nt"></option></span> <span class="nt"><option</span> <span class="na">value=</span><span class="s">'90'</span><span class="nt">></span>90<span class="nt"></option></span> <span class="nt"><option</span> <span class="na">value=</span><span class="s">'100'</span><span class="nt">></span>100<span class="nt"></option></span> <span class="nt"></select></span> <span class="nt"></p></span> <span class="nt"><p></span> <span class="nt"><button</span> <span class="na">x-on:click=</span><span class="s">"users.push(newUser); newUser = {name: '', age: 10};"</span><span class="nt">></span>Add User<span class="nt"></button></span> <span class="nt"></p></span> <span class="nt"></div></span> <span class="nt"></div></span> </code></pre></div> <p>Like I said, this is about as complex as you’d want to get with Alpine. But I think that’s a feature, not a bug. Sometimes when you use something as powerful as React or Vue, it’s tempting to push more and more of your app into that paradigm, all while forgetting that there are rough edges around doing so.</p>