JavaScript Sprinkles with AlpineJS

<p>I&rsquo;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 &ldquo;sprinkles&rdquo; of JavaScript. In the past, I&rsquo;ve leaned on <a href="https://jquery.com">jQuery</a> for this, but I&rsquo;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&rsquo;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">&lt;div</span> <span class="na">x-data=</span><span class="s">"{isOpen: false}"</span><span class="nt">&gt;</span> <span class="nt">&lt;button</span> <span class="na">x-on:click=</span><span class="s">"isOpen = true"</span><span class="nt">&gt;</span>Show Content<span class="nt">&lt;/button&gt;</span> <span class="nt">&lt;p</span> <span class="na">x-show=</span><span class="s">"isOpen"</span><span class="nt">&gt;</span>Content goes here.<span class="nt">&lt;/p&gt;</span> <span class="nt">&lt;/div&gt;</span> </code></pre></div> <p>That&rsquo;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&rsquo;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&rsquo;s move on to a slightly more complex example. Here we&rsquo;ll keep the value of a text input in sync with the text in a <code>&lt;p&gt;</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">&lt;div</span> <span class="na">x-data=</span><span class="s">"{fullName: ''}"</span><span class="nt">&gt;</span> <span class="nt">&lt;label&gt;</span>Full Name<span class="nt">&lt;/label&gt;</span> <span class="nt">&lt;input</span> <span class="na">x-model=</span><span class="s">"fullName"</span> <span class="nt">/&gt;</span> <span class="nt">&lt;p</span> <span class="na">x-text=</span><span class="s">'fullName'</span><span class="nt">&gt;&lt;/p&gt;</span> <span class="nt">&lt;/div&gt;</span> </code></pre></div> <p>But let&rsquo;s say we want to prefix the name with &ldquo;My name is&rdquo; and only show it when the name has been filled in. We&rsquo;ll use the <code>x-if</code> attribute on a <code>&lt;template&gt;</code> tag.</p> <div class="highlight"><pre class="highlight html"><code><span class="nt">&lt;div</span> <span class="na">x-data=</span><span class="s">"{fullName: ''}"</span><span class="nt">&gt;</span> <span class="nt">&lt;label&gt;</span>Full Name<span class="nt">&lt;/label&gt;</span> <span class="nt">&lt;input</span> <span class="na">x-model=</span><span class="s">"fullName"</span> <span class="nt">/&gt;</span> <span class="nt">&lt;template</span> <span class="na">x-if=</span><span class="s">"fullName.length &gt; 0"</span><span class="nt">&gt;</span> <span class="nt">&lt;p&gt;</span>My name is <span class="nt">&lt;span</span> <span class="na">x-text=</span><span class="s">'fullName'</span><span class="nt">&gt;&lt;/span&gt;&lt;/p&gt;</span> <span class="nt">&lt;/template&gt;</span> <span class="nt">&lt;/div&gt;</span> </code></pre></div> <p>Lastly, let&rsquo;s do about as complex of an example as I&rsquo;d recommend with Alpine. We&rsquo;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">&lt;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">&gt;</span> <span class="nt">&lt;table&gt;</span> <span class="nt">&lt;thead&gt;</span> <span class="nt">&lt;tr&gt;</span> <span class="nt">&lt;th&gt;</span>Name<span class="nt">&lt;/th&gt;</span> <span class="nt">&lt;th&gt;</span>Age<span class="nt">&lt;/th&gt;</span> <span class="nt">&lt;/tr&gt;</span> <span class="nt">&lt;/thead&gt;</span> <span class="nt">&lt;tbody&gt;</span> <span class="nt">&lt;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">&gt;</span> <span class="nt">&lt;tr&gt;</span> <span class="nt">&lt;td</span> <span class="na">x-text=</span><span class="s">"user.name"</span> <span class="nt">/&gt;</span> <span class="nt">&lt;td</span> <span class="na">x-text=</span><span class="s">"user.age"</span> <span class="nt">/&gt;</span> <span class="nt">&lt;/tr&gt;</span> <span class="nt">&lt;/template&gt;</span> <span class="nt">&lt;/tbody&gt;</span> <span class="nt">&lt;/table&gt;</span> <span class="nt">&lt;div&gt;</span> <span class="nt">&lt;p&gt;</span> <span class="nt">&lt;label</span> <span class="na">for=</span><span class="s">'name'</span><span class="nt">&gt;</span>Name<span class="nt">&lt;/label&gt;</span> <span class="nt">&lt;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">/&gt;</span> <span class="nt">&lt;/p&gt;</span> <span class="nt">&lt;p&gt;</span> <span class="nt">&lt;label</span> <span class="na">for=</span><span class="s">'age'</span><span class="nt">&gt;</span>Age<span class="nt">&lt;/label&gt;</span> <span class="nt">&lt;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">&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">'10'</span><span class="nt">&gt;</span>10<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">'20'</span><span class="nt">&gt;</span>20<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">'30'</span><span class="nt">&gt;</span>30<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">'40'</span><span class="nt">&gt;</span>40<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">'50'</span><span class="nt">&gt;</span>50<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">'60'</span><span class="nt">&gt;</span>60<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">'70'</span><span class="nt">&gt;</span>70<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">'80'</span><span class="nt">&gt;</span>80<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">'90'</span><span class="nt">&gt;</span>90<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;option</span> <span class="na">value=</span><span class="s">'100'</span><span class="nt">&gt;</span>100<span class="nt">&lt;/option&gt;</span> <span class="nt">&lt;/select&gt;</span> <span class="nt">&lt;/p&gt;</span> <span class="nt">&lt;p&gt;</span> <span class="nt">&lt;button</span> <span class="na">x-on:click=</span><span class="s">"users.push(newUser); newUser = {name: '', age: 10};"</span><span class="nt">&gt;</span>Add User<span class="nt">&lt;/button&gt;</span> <span class="nt">&lt;/p&gt;</span> <span class="nt">&lt;/div&gt;</span> <span class="nt">&lt;/div&gt;</span> </code></pre></div> <p>Like I said, this is about as complex as you&rsquo;d want to get with Alpine. But I think that&rsquo;s a feature, not a bug. Sometimes when you use something as powerful as React or Vue, it&rsquo;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>