Stimulus
A modest JavaScript framework that adds just enough behavior to server-rendered HTML you already have.
Repository Health
Technical Analysis
Stimulus is Basecamp’s answer to the question of how much JavaScript framework a server-rendered app actually needs. Rather than owning rendering, routing, or state like a full SPA framework, it watches the DOM for data-controller, data-action, and data-*-target attributes and wires up small Controller classes to bring that HTML to life. There’s no templating language, no virtual DOM, and no build-your-whole-frontend-around-me philosophy — HTML stays the source of truth and Stimulus reacts to it.
It pairs with Turbo (the other half of the Hotwire stack) to deliver fast, mostly-server-rendered applications with a fraction of the JavaScript a typical SPA requires, and is the default frontend approach for new Rails applications since Rails 7.
What You Get
- A
Controllerbase class withinitialize/connect/disconnectlifecycle hooks tied to DOM presence via MutationObserver - Declarative
targets,values,classes, andoutletsstatic properties that auto-generate typed getters/setters on your controller - An action system (
data-action="click->hello#greet") that binds DOM events to controller methods without manualaddEventListenercalls - Automatic connect/disconnect as elements are added or removed from the DOM, including Turbo page navigations and Ajax-injected HTML
- A zero-dependency runtime distributed as ESM, UMD, and a drop-in
<script>tag build for no-build-step usage - Full TypeScript typings out of the box, including generic
Controller<ElementType>for typedthis.element
Common Use Cases
- Adding interactive behavior (toggles, autocomplete, drag-and-drop, form validation) to a Rails, Django, Laravel, or other server-rendered app
- Progressively enhancing existing HTML pages without adopting a full SPA architecture or client-side router
- Building the JavaScript layer of a Hotwire (Turbo + Stimulus) application for fast, mostly-server-rendered UX
- Replacing scattered jQuery/vanilla-JS event-binding code with a structured, testable controller pattern
- Wiring up widgets embedded in CMS-rendered or email-template-style HTML where a build step isn’t practical
Under The Hood
Architecture
Stimulus layers cleanly from Application down: Application owns a Dispatcher (event binding) and a Router (identifier-to-controller wiring), the Router drives a ScopeObserver/Module/Context chain that creates one Context per matched element-plus-identifier pair, and each Context instantiates the user’s Controller subclass and exposes scope, targets, values, and outlets through small dedicated observer classes (TargetObserver, ValueObserver, OutletObserver) built on MutationObserver primitives in src/mutation-observers. A custom Multimap/IndexedMultimap (src/multimap) backs the identifier-to-scope and event-listener bookkeeping. The design cleanly separates DOM observation from controller lifecycle from event dispatch, so swapping any one layer (e.g. the attribute schema in schema.ts) doesn’t ripple through the others.
Tech Stack
Written entirely in TypeScript (99% of the codebase) with zero runtime dependencies, compiled via tsc for type declarations and bundled with Rollup (rollup-plugin-terser for minification) into ESM and UMD outputs plus a standalone <script> build. There is no framework dependency at all — it operates directly on the DOM via native MutationObserver, CustomEvent, and WeakMap/Map APIs, which is what lets it ship as a true zero-dependency package.
Code Quality
The test suite (46 files under src/tests) runs on Karma against real browsers (Chrome and Firefox launchers configured in karma.conf.cjs) using QUnit assertions, exercising controllers, targets, values, outlets, and action parsing individually. ESLint is configured with @typescript-eslint rules plus Prettier integration, and CI (.github/workflows) runs the build, type-check, and lint across Node 18 through 21 on every push and PR. Naming is consistent and the public surface is fully typed, including override points like static blessings for extension.
API Design
Getting started requires no build tooling: a data-controller attribute plus a class extending Controller is a working integration. Declaring static targets/static values auto-generates conventionally-named properties (this.nameTarget, this.countValue) instead of requiring manual querySelector or attribute parsing, and the action descriptor mini-language (click->identifier#method) reads naturally in HTML. Documentation is unusually thorough for a project this size: an eight-chapter handbook plus a full API reference for actions, targets, values, outlets, CSS classes, and lifecycle callbacks.
Used by 5 apps in this directory
Authgear
Authentication
Open-source, self-hostable authentication platform with passkeys, biometric login, SSO, MFA, and GraphQL admin API — a full Auth0/Clerk/Firebase alternative for SaaS and mobile apps.
BillaBear
Ecommerce · Invoicing Finance
Self-hostable subscription management and billing platform with Stripe integration, tax automation, configurable workflows, and Twig-based document generation.
Forem
Community · Blogging
Open source Ruby on Rails platform for building developer communities with articles, discussions, and social profiles—the same software that powers dev.to.
OpenProject
Project Management · Productivity · Collaboration
The open source project management platform that unifies Gantt charts, agile boards, time tracking, and team collaboration under full self-hosted control.
wallabag
Bookmarks Archiving
Self-hosted read-it-later app that saves clean, ad-free articles from any webpage for distraction-free reading across all your devices.