DEV Community

Alex Aslam
Alex Aslam

Posted on

Alpine.js vs. Stimulus: A Pragmatic Comparison

"We replaced 8,000 lines of Stimulus with Alpine.js—then switched back. Here’s what we learned."

Both Alpine.js and Stimulus promise minimal JavaScript for maximal interactivity, but their philosophies lead to wildly different developer experiences.

After 18 months of using both in production, we discovered:

  • Alpine.js is like a Swiss Army knife—quick, all-in-one, but risky at scale.
  • Stimulus is more like a surgeon’s scalpel—precise, reusable, but slower to start.

Here’s how to choose (or mix them) without regret.


1. Core Philosophies

Alpine.js

"Write JS directly in your HTML."
Pros:

  • Instant gratification (x-data, x-show)
  • Built-in transitions (x-transition)
  • No build step

Cons:

  • Temptation to write logic in markup
  • Hard to debug at scale

Stimulus

"Connect HTML to reusable controllers."
Pros:

  • Encourages separation of concerns
  • TypeScript-friendly
  • Testable with Jest

Cons:

  • More boilerplate
  • Steeper learning curve

2. Side-by-Side Comparison

Example: A Counter Component

Alpine.js:

<div x-data="{ count: 0 }">
  <button @click="count++">+</button>
  <span x-text="count"></span>
</div>
Enter fullscreen mode Exit fullscreen mode

Stimulus:

<div data-controller="counter">
  <button data-action="counter#increment">+</button>
  <span data-counter-target="output">0</span>
</div>
Enter fullscreen mode Exit fullscreen mode
// app/javascript/controllers/counter_controller.js
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["output"]

  increment() {
    this.outputTarget.textContent =
      parseInt(this.outputTarget.textContent) + 1
  }
}
Enter fullscreen mode Exit fullscreen mode

Key Difference:

  • Alpine embeds logic in HTML
  • Stimulus separates logic into controllers

3. When to Choose Alpine.js

Prototyping (get interactive fast)
Small projects (admin dashboards, marketing sites)
When you hate build steps

Ideal Use Case:

<!-- Alpine shines here -->
<div
  x-data="{ open: false }"
  x-show="open"
  x-transition
>
  <button @click="open = !open">Toggle</button>
</div>
Enter fullscreen mode Exit fullscreen mode

4. When to Choose Stimulus

Large codebases (needs maintainability)
Teams with React/Vue experience (familiar patterns)
When TypeScript matters

Ideal Use Case:

// Stimulus scales better
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["input", "preview"]

  updatePreview() {
    this.previewTarget.textContent = this.inputTarget.value
  }
}
Enter fullscreen mode Exit fullscreen mode

5. The Hybrid Approach

How We Use Both

Tool When We Use It
Alpine.js Quick UI tricks (toggles, tabs)
Stimulus Complex logic (forms, drag/drop)

Example:

<!-- Alpine for UI state -->
<div x-data="{ activeTab: 'profile' }">
  <!-- Stimulus for heavy lifting -->
  <form data-controller="profile-form">
    <input data-action="profile-form#validate">
  </form>
</div>
Enter fullscreen mode Exit fullscreen mode

6. Key Takeaways

🔹 Alpine.js = speed upfront, pain later
🔹 Stimulus = slow start, smooth scaling
🔹 Best combo: Alpine for sprinkles, Stimulus for depth


"But Our Team Knows Vue!"

Stick with what works! Here’s how we’d decide:

  1. Try Alpine for one small feature
  2. Compare dev velocity
  3. Gradually introduce Stimulus for complex parts

Team Alpine or Team Stimulus? Fight it out below 👇

Top comments (0)