AlpineJS Hidden Gems

Published September 29th, 2020
12 minute read
Warning!
This was written over two years ago, so some information might be outdated. Frameworks and best practices change. The web moves fast! You may need to adjust a few things if you follow this article word for word.

It's no secret — I'm a huge fan of Alpine. For me, it hits that goldilocks zone between minimalistic and powerful. Alpine is straightforward to get started with, especially if you have a VueJS background. However, there are a few hidden features (okay, they're documented, not actually hidden) that you may not notice if you're just quickly scanning the readme.

Easy Show / Hide Transitions

Alpine includes support for custom transition classes via x-transition, but did you know there's also an easier way to add show/hide transitions to your components? The x-show directive also has an optional modifier transition. Check it out:

1<!-- no transition -->
2<div x-show="open">I will appear abruptly with no transition.</div>
3 
4<!-- easy fade + scale -->
5<div x-show.transition="open">I come and go with a nice transition.</div>
1<!-- no transition -->
2<div x-show="open">I will appear abruptly with no transition.</div>
3 
4<!-- easy fade + scale -->
5<div x-show.transition="open">I come and go with a nice transition.</div>

Beats having six lines of x-transition class mappings for 80% of cases. However, it doesn't stop there! The transition modifier on x-show also has other optional modifiers.

1<!-- Modify the transition -->
2<div x-show.transition.in="open">Only transition in.</div>
3<div x-show.transition.out="open">Only transition out.</div>
4<div x-show.transition.opacity="open">Only fade (no scaling).</div>
5<div x-show.transition.scale="open">Only scaling (no fade).</div>
6<div x-show.transition.duration.2000ms="open">
7 Transition in for two seconds. Out for half of that.
8</div>
9<div x-show.transition.origin.top.left="open">
10 Transition starts and ends from the top left
11</div>
12 
13<!-- Combine modifiers. Go crazy! -->
14<div x-show.transition.in.origin.top.left.opacity="open">
15 Only transition in from the top left with a fade.
16</div>
1<!-- Modify the transition -->
2<div x-show.transition.in="open">Only transition in.</div>
3<div x-show.transition.out="open">Only transition out.</div>
4<div x-show.transition.opacity="open">Only fade (no scaling).</div>
5<div x-show.transition.scale="open">Only scaling (no fade).</div>
6<div x-show.transition.duration.2000ms="open">
7 Transition in for two seconds. Out for half of that.
8</div>
9<div x-show.transition.origin.top.left="open">
10 Transition starts and ends from the top left
11</div>
12 
13<!-- Combine modifiers. Go crazy! -->
14<div x-show.transition.in.origin.top.left.opacity="open">
15 Only transition in from the top left with a fade.
16</div>

For more examples, check out the full documentation of x-show.

Camel Modifiers

Both x-bind and x-on support a .camel modifier. I find the latter one is more useful (especially when working with Livewire), but they're both good to know about.

x-bind

1<div x-bind:attribute-name.camel="someVar">...</div>
2 
3<!-- Result -->
4<div attributeName="(value of someVar)">...</div>
1<div x-bind:attribute-name.camel="someVar">...</div>
2 
3<!-- Result -->
4<div attributeName="(value of someVar)">...</div>

x-on

1<div x-on:your-event.camel="doSomething">
2 <!-- Result -->
3 <div @yourEvent="doSomething"></div>
4</div>
1<div x-on:your-event.camel="doSomething">
2 <!-- Result -->
3 <div @yourEvent="doSomething"></div>
4</div>

Model Modifiers

There are two modifiers you can use with x-modeldebounce and number. Both can be useful in the right situations. I didn't know that number existed before writing this article!

Debounce

Delay updating the binding until a certain amount of time has passed since inputting something. In other words, when you stop typing it waits a little bit before updating the data. By default the time is 250ms, but you can customize the debounce time if you wish.

1<input x-model.debounce="name" />
2<!-- 250ms default -->
3<input x-model.debounce.500="name" />
4<!-- 500ms -->
5<input x-model.debounce.500ms="name" />
6<!-- 500ms -->
1<input x-model.debounce="name" />
2<!-- 250ms default -->
3<input x-model.debounce.500="name" />
4<!-- 500ms -->
5<input x-model.debounce.500ms="name" />
6<!-- 500ms -->

Number

This modifier tries to cast the value to a number instead of the string representation of that number. If it can't cast it easily to a number, the original value is returned unmodified.

1<input x-model.number="age" />
1<input x-model.number="age" />

The x-spread Operator

When you start to create more complex components, separating the logic and the template is a great pattern. If you come from a Vue background, you'll know this as single file components (.vue files). Separating template and logic is a great step, but the x-spread object lets you take it even further by encapsulating the attribute bindings too! Take this component example:

1<div x-data="dropdown()">
2 <button @click="show">Open</button>
3 
4 <div x-show="open" @click.away="hide">
5 Dropdown contents
6 </div>
7</div>
8 
9<script>
10 function dropdown() {
11 return {
12 open: false,
13 show() { this.open = true },
14 hide() { this.open = false },
15 }
16 }
17</script>
1<div x-data="dropdown()">
2 <button @click="show">Open</button>
3 
4 <div x-show="open" @click.away="hide">
5 Dropdown contents
6 </div>
7</div>
8 
9<script>
10 function dropdown() {
11 return {
12 open: false,
13 show() { this.open = true },
14 hide() { this.open = false },
15 }
16 }
17</script>

Here's how it might look if we used x-spread.

1<div x-data="dropdown()">
2 <button x-spread="trigger">Open</button>
3 
4 <div x-spread="contents">
5 Dropdown contents
6 </div>
7</div>
8 
9<script>
10 function dropdown() {
11 return {
12 open: false,
13 trigger: {
14 ['@click']() { this.open = true }
15 }
16 contents: {
17 ['x-show']() { return this.open },
18 ['@click.away']() {
19 this.open = false
20 },
21 }
22 }
23 }
24</script>
1<div x-data="dropdown()">
2 <button x-spread="trigger">Open</button>
3 
4 <div x-spread="contents">
5 Dropdown contents
6 </div>
7</div>
8 
9<script>
10 function dropdown() {
11 return {
12 open: false,
13 trigger: {
14 ['@click']() { this.open = true }
15 }
16 contents: {
17 ['x-show']() { return this.open },
18 ['@click.away']() {
19 this.open = false
20 },
21 }
22 }
23 }
24</script>

I tried to keep the example small for this article, but you can imagine how useful this might be when you have many attributes on a component. You no longer have to cross-reference the HTML and the Javascript sections of components to figure out what's happening. Instead, x-spread allows you to put everything in one spot.

Thanks for reading!

Enjoy this article? Follow me on Twitter for more tips, articles and links.
😢 Awww, nobody has liked or mentioned this on Twitter yet.

Want Updates?

Sign up here if you want to stay in the loop about new articles or products I'm making.
I'll never spam you. Unsubscribe at any time.
Copyright ©2024 Austen Cameron