Skip to main content
CodeSpeedTest
Languages
Start TypingJump into a test — pick any languageAdaptive TrainingUnlock chars as you master themPractice DrillsFocused sessions targeting weak spotsDaily ChallengesNew coding challenges every dayRace ModeCompete against others in real timeAI OpponentRace against an AI at your WPM levelTournamentsLive coding speed tournamentsArcade GamesZType, Overkill Survival, Glyphica & moreGamificationXP, coins, badges & quests
LeaderboardGlobal rankings for every languageCertificatesEarn verifiable Bronze / Silver / Gold certsActivityDaily streaks & historical analyticsProfileYour stats, badges & achievements
Browse Languages500+ languages with real code examplesBlogTips, guides & deep divesFree ToolsWPM calculator, typing speed report & moreFAQCommon questions answeredGetting StartedNew to CodeSpeedTest?AboutOur story & missionSupportGet help — Pro users get priorityContactGet in touch with the team
Pricing
  1. Home
  2. /
  3. Learn
  4. /
  5. Stimulus-js

Learn Stimulus-js - 9 Code Examples & CST Typing Practice Test

Stimulus.js is a modest JavaScript framework designed to enhance static HTML by connecting elements to JavaScript controllers. It complements server-rendered HTML, making it ideal for modestly interactive UIs without a heavy frontend framework.

View all 9 Stimulus-js code examples →
Stimulus.js Counter ControllerStimulus.js Counter with Step IncrementStimulus.js Counter with Max LimitStimulus.js Counter with Auto-ResetStimulus.js Counter with HistoryStimulus.js Counter with Dark Mode OnlyStimulus.js Counter with Auto-IncrementStimulus.js Counter with Conditional ThemeStimulus.js Full Featured Counter

Learn STIMULUS-JS with Real Code Examples

Updated Nov 22, 2025

Explain

Stimulus adds behavior to HTML via data attributes and controllers.

It emphasizes convention over configuration for fast, maintainable development.

Stimulus controllers respond to events without replacing HTML markup.

Core Features

Stimulus.Controller

Targets for DOM elements

Actions for events

Values for reactive state

Lifecycle callbacks

Basic Concepts Overview

Controllers manage elements and logic

Targets connect HTML elements to controllers

Actions bind DOM events to controller methods

Values provide reactive data

Lifecycle callbacks for initialization and cleanup

Project Structure

controllers/ - Stimulus controllers

index.js - entry point & app initialization

views/ - HTML templates

assets/ - CSS & JS assets

public/ - static files

Building Workflow

Define a controller class

Declare targets and values

Connect actions in HTML

Respond to user events

Manage reactive state via values

Difficulty Use Cases

Beginner: simple form interactions

Intermediate: multiple controllers with targets

Advanced: stateful widget coordination

Expert: integrating with Turbo streams

Community: plugins and extensions

Comparisons

Lighter than React/Vue

More declarative than jQuery alone

Focused on server-rendered HTML

No SPA abstractions

Complementary to Turbo/Hotwire

Versioning Timeline

2016 - Initial release by Basecamp

2017 - Early adoption in Rails apps

2019 - Hotwire & Turbo integration

2021 - Stable community adoption

2025 - Continued maintenance and modern ES support

Glossary

Controller: JS class connected to HTML

Target: DOM element reference

Action: Event binding via HTML

Value: Reactive data for a controller

Connect: Lifecycle callback for attachment

Installation Setup

Install via npm: `npm install @hotwired/stimulus`

Or include via CDN

Initialize Application in `index.js`

Register controllers manually or auto-load

Include controllers via ES modules

Environment Setup

Install Node.js

Install Stimulus via npm or CDN

Configure module bundler

Set up controllers folder

Link HTML elements via data attributes

Config Files

index.js - app initialization

controllers/*.js - Stimulus controllers

views/*.html - templates

assets/*.css/js - static assets

package.json - dependencies & scripts

Cli Commands

No official CLI

Use npm scripts for bundling

Use Webpack/ESBuild for assets

Rails generators for controllers

Include via CDN in static sites

Internationalization

No built-in i18n

Use Rails or JS libraries

Dynamic text via values

Template-based localization

Custom JS for multi-locale handling

Accessibility

Use semantic HTML

ARIA attributes manually

Accessible event handling

Focus management with actions

Screen reader testing

Ui Styling

CSS frameworks like Tailwind

Scoped CSS for widgets

HTML-first templates

Dynamic class toggling via targets

Minimal JS for styling

State Management

Controller values

DOM targets for state

Event-driven updates

Inter-controller communication manually

Integration with Turbo streams

Data Management

Fetch APIs from controllers

Use Turbo streams for updates

Reactive values for state

No built-in global store

Manual REST handling via fetch/XHR

Architecture

Controller-centric

Event-driven with HTML actions

Declarative data bindings via targets

Values for controller state

No virtual DOM or reactive diffing

Rendering Model

DOM-driven updates

Declarative event binding via actions

Reactive values for state

Lifecycle callbacks for initialization and cleanup

No virtual DOM

Architectural Patterns

Controller-per-element or per-widget

Target-driven DOM references

Action-based event handling

Value-driven reactive state

HTML-first progressive enhancement

Real World Architectures

Rails apps with Turbo + Stimulus

Server-rendered CMS platforms

Dynamic dashboards with light JS

Interactive forms and modals

Admin panels with minimal SPA complexity

Design Principles

Enhance HTML instead of replacing it

Controller-based structure

Convention over configuration

Minimal footprint

Progressive enhancement first

Scalability Guide

Use one controller per widget

Keep targets limited

Avoid global state

Use values for isolated reactive state

Combine with Turbo for larger apps

Migration Guide

Enhance HTML progressively instead of replacing it

Convert jQuery widgets to controllers

Use Turbo frames and streams for dynamic updates

Replace inline JS with controller methods

Modularize behaviors into controllers

Performance Notes

Tiny payload (<3KB gzipped)

Minimal runtime overhead

No virtual DOM diffing

DOM manipulation only where needed

Fast load times for server-rendered pages

Security Notes

Sanitize inputs in controllers

Avoid inline JavaScript injection

Validate user data on server

Use HTTPS

Limit DOM manipulation to controlled elements

Monitoring Analytics

Sentry for JS errors

Browser console logs

Hotwire logs for Turbo updates

Custom analytics hooks via controllers

Performance monitoring via Chrome DevTools

Code Quality

Use ESLint/Prettier

Organize controllers modularly

Keep targets explicit and unique

Test controller methods with Jest

Document actions and values

Practical Examples

Expandable/collapsible UI panels

Dynamic form validation

Carousel/slider interactions

Live search filtering

Turbo-driven content updates

Troubleshooting

Ensure controllers are registered correctly

Check target naming and spelling

Verify actions match method names

Inspect controller connection state

Debug reactive values via console

Testing Guide

Jest or Mocha for unit tests

Test controller methods

Mock DOM elements with jsdom

Simulate actions/events

Snapshot controller outputs if needed

Deployment Options

Static hosting

Rails server

Node-based servers

CDN inclusion

Turbo/Hotwire integrated apps

Tools Ecosystem

StimulusReflex (Rails enhancement)

Hotwire integration

Turbo Frames & Streams

Webpack / ESBuild

Rails asset pipeline

Integrations

Rails with Hotwire

Turbo Frames & Streams

Tailwind CSS or other CSS frameworks

jQuery (if needed)

Third-party JS widgets

Productivity Tips

Use `data-action` consistently

Keep controllers small and focused

Leverage values for reactive data

Combine with Turbo frames for dynamic updates

Document controller responsibilities

Challenges

Create dynamic form interactions

Build a collapsible menu widget

Implement live search with Stimulus

Coordinate multiple controllers on one page

Integrate Stimulus with Turbo Streams updates

Learning Path

Understand controller, target, and action concepts

Learn lifecycle callbacks

Use values for reactive state

Integrate with Turbo frames and streams

Build small interactive widgets

Skill Improvement Plan

Week 1: Basic controller setup

Week 2: Targets & actions

Week 3: Values & lifecycle callbacks

Week 4: Turbo integration

Week 5: Advanced inter-controller communication

Interview Questions

What is Stimulus.js?

How do controllers, targets, and actions work?

What are values in Stimulus?

How does Stimulus differ from React or Vue?

How do lifecycle callbacks operate?

Cheat Sheet

`Controller` - base class for behavior

`static targets` - DOM element references

`data-action` - bind events

`values` - reactive state

`connect()` - called when controller attaches

Books

StimulusJS in Action

Modern Rails with Hotwire

Progressive Enhancement with Stimulus

Building Interactive UIs with Rails

Hotwire + Stimulus Guide for Developers

Tutorials

Stimulus official tutorials

Hotwire Rails guides

GoRails Stimulus series

Net Ninja Stimulus video tutorials

YouTube & blog posts for Rails + Stimulus integration

Official Docs

https://stimulus.hotwired.dev

https://hotwired.dev

Community Links

Stimulus GitHub

Hotwire GitHub

StackOverflow

Reddit r/rails

Hotwire Slack community

Community Support

Stimulus GitHub

Hotwire community

StackOverflow

Rails discourse forums

Reddit r/rails & r/javascript

Monetization

Rails-based SaaS dashboards

Admin panel templates

Stimulus plugin development

Legacy app maintenance

UI component kits

Future Roadmap

Maintain lightweight footprint

Improve TypeScript support

Better Turbo integration

Community-driven plugin ecosystem

Modern ES module support

When Not To Use

Large-scale SPAs

Apps needing client-side routing

Complex state management

Real-time dashboards

Heavy JS-driven interactions

Final Summary

Stimulus.js is a lightweight framework to enhance HTML with behavior.

Controller-based architecture keeps JS organized and declarative.

It excels in server-rendered apps with minimal JS overhead.

Perfect for Rails, Turbo, and progressive enhancement.

Allows interactive UIs without full SPA complexity.

Faq

Is Stimulus a SPA framework?

No, it enhances static HTML pages.

Does Stimulus replace jQuery?

Not strictly, but it provides modern declarative behavior.

Is Stimulus lightweight?

Yes - about 3 KB gzipped.

Can Stimulus work with Rails?

Yes, it integrates tightly with Turbo/Hotwire.

Does Stimulus support TypeScript?

Yes, official typings exist.

Code Sample Descriptions

1

Stimulus.js Counter Controller

<div data-controller="counter" data-counter-dark-value="false" data-counter-count-value="0">
    <h2>Counter: <span data-counter-target="count">0</span></h2>
    <div>
        <button data-action="click->counter#increment">+</button>
        <button data-action="click->counter#decrement">-</button>
        <button data-action="click->counter#reset">Reset</button>
    </div>
    <button data-action="click->counter#toggleTheme">Switch Theme</button>
</div>

<script>
    import { Application } from '@hotwired/stimulus';
    const application = Application.start();

    application.register('counter', class extends Stimulus.Controller {
        static targets = ['count'];
        static values = { count: Number, dark: Boolean };
        connect() { this.updateDisplay(); }
        increment() { this.countValue++; this.updateDisplay(); }
        decrement() { this.countValue--; this.updateDisplay(); }
        reset() { this.countValue = 0; this.updateDisplay(); }
        toggleTheme() { this.darkValue = !this.darkValue; this.updateDisplay(); }
        updateDisplay() { this.countTarget.textContent = this.countValue; this.element.className = this.darkValue ? 'dark-theme' : 'light-theme'; }
    });
</script>

<style>
.dark-theme { background-color: #222; color: #eee; }
.light-theme { background-color: #fff; color: #000; }
</style>

Basic counter using Stimulus.js with dark/light theme toggle.

Let’s Try →
2

Stimulus.js Counter with Step Increment

<div data-controller="counter" data-counter-count-value="0" data-counter-step-value="2">
    <h2>Counter: <span data-counter-target="count">0</span></h2>
    <div>
        <button data-action="click->counter#increment">+</button>
        <button data-action="click->counter#decrement">-</button>
        <button data-action="click->counter#reset">Reset</button>
    </div>
</div>
<script>
    import { Application } from '@hotwired/stimulus';
    const application = Application.start();
    application.register('counter', class extends Stimulus.Controller {
        static targets = ['count'];
        static values = { count: Number, step: Number };
        connect() { this.updateDisplay(); }
        increment() { this.countValue += this.stepValue; this.updateDisplay(); }
        decrement() { this.countValue -= this.stepValue; this.updateDisplay(); }
        reset() { this.countValue = 0; this.updateDisplay(); }
        updateDisplay() { this.countTarget.textContent = this.countValue; }
    });
</script>

Counter increments/decrements by a specified step value.

Let’s Try →
3

Stimulus.js Counter with Max Limit

<div data-controller="counter" data-counter-count-value="0" data-counter-max-value="5">
    <h2>Counter: <span data-counter-target="count">0</span></h2>
    <div>
        <button data-action="click->counter#increment">+</button>
        <button data-action="click->counter#decrement">-</button>
        <button data-action="click->counter#reset">Reset</button>
    </div>
</div>
<script>
    import { Application } from '@hotwired/stimulus';
    const application = Application.start();
    application.register('counter', class extends Stimulus.Controller {
        static targets = ['count'];
        static values = { count: Number, max: Number };
        connect() { this.updateDisplay(); }
        increment() { if(this.countValue < this.maxValue) this.countValue++; this.updateDisplay(); }
        decrement() { this.countValue--; this.updateDisplay(); }
        reset() { this.countValue = 0; this.updateDisplay(); }
        updateDisplay() { this.countTarget.textContent = this.countValue; }
    });
</script>

Stops incrementing after a maximum count is reached.

Let’s Try →
4

Stimulus.js Counter with Auto-Reset

<div data-controller="counter" data-counter-count-value="0" data-counter-threshold-value="10">
    <h2>Counter: <span data-counter-target="count">0</span></h2>
    <div>
        <button data-action="click->counter#increment">+</button>
        <button data-action="click->counter#decrement">-</button>
        <button data-action="click->counter#reset">Reset</button>
    </div>
</div>
<script>
    import { Application } from '@hotwired/stimulus';
    const application = Application.start();
    application.register('counter', class extends Stimulus.Controller {
        static targets = ['count'];
        static values = { count: Number, threshold: Number };
        connect() { this.updateDisplay(); }
        increment() { this.countValue = (this.countValue + 1 > this.thresholdValue) ? 0 : this.countValue + 1; this.updateDisplay(); }
        decrement() { this.countValue--; this.updateDisplay(); }
        reset() { this.countValue = 0; this.updateDisplay(); }
        updateDisplay() { this.countTarget.textContent = this.countValue; }
    });
</script>

Automatically resets when counter exceeds threshold.

Let’s Try →
5

Stimulus.js Counter with History

<div data-controller="counter" data-counter-count-value="0">
    <h2>Counter: <span data-counter-target="count">0</span></h2>
    <div>History: <span data-counter-target="history"></span></div>
    <div>
        <button data-action="click->counter#increment">+</button>
        <button data-action="click->counter#decrement">-</button>
        <button data-action="click->counter#reset">Reset</button>
    </div>
</div>
<script>
    import { Application } from '@hotwired/stimulus';
    const application = Application.start();
    application.register('counter', class extends Stimulus.Controller {
        static targets = ['count', 'history'];
        static values = { count: Number };
        history = [];
        connect() { this.updateDisplay(); }
        increment() { this.countValue++; this.history.push('Increment'); this.updateDisplay(); }
        decrement() { this.countValue--; this.history.push('Decrement'); this.updateDisplay(); }
        reset() { this.countValue=0; this.history.push('Reset'); this.updateDisplay(); }
        updateDisplay() { this.countTarget.textContent=this.countValue; this.historyTarget.textContent=this.history.join(', '); }
    });
</script>

Tracks increment/decrement history.

Let’s Try →
6

Stimulus.js Counter with Dark Mode Only

<div data-controller="counter" data-counter-dark-value="false">
    <h2>Counter: 0</h2>
    <button data-action="click->counter#toggleTheme">Toggle Theme</button>
</div>
<script>
    import { Application } from '@hotwired/stimulus';
    const application = Application.start();
    application.register('counter', class extends Stimulus.Controller {
        static values = { dark: Boolean };
        connect() {}
        toggleTheme() { this.darkValue = !this.darkValue; this.element.className = this.darkValue ? 'dark-theme' : 'light-theme'; }
    });
</script>
<style>.dark-theme{background:#222;color:#eee}.light-theme{background:#fff;color:#000}</style>

Static counter with only dark/light theme toggle.

Let’s Try →
7

Stimulus.js Counter with Auto-Increment

<div data-controller="counter" data-counter-count-value="0">
    <h2>Counter: <span data-counter-target="count">0</span></h2>
</div>
<script>
    import { Application } from '@hotwired/stimulus';
    const application = Application.start();
    application.register('counter', class extends Stimulus.Controller {
        static targets = ['count'];
        static values = { count: Number };
        connect() { this.interval = setInterval(()=>{ this.countValue++; this.updateDisplay(); },1000); }
        disconnectedCallback() { clearInterval(this.interval); }
        updateDisplay() { this.countTarget.textContent = this.countValue; }
    });
</script>

Automatically increments counter every second.

Let’s Try →
8

Stimulus.js Counter with Conditional Theme

<div data-controller="counter" data-counter-count-value="0">
    <h2>Counter: <span data-counter-target="count">0</span></h2>
    <button data-action="click->counter#increment">+</button>
</div>
<script>
    import { Application } from '@hotwired/stimulus';
    const application = Application.start();
    application.register('counter', class extends Stimulus.Controller {
        static targets = ['count'];
        static values = { count: Number };
        connect() { this.updateDisplay(); }
        increment() { this.countValue++; this.updateDisplay(); }
        updateDisplay() { this.element.className = (this.countValue%2===0) ? 'dark-theme' : 'light-theme'; this.countTarget.textContent=this.countValue; }
    });
</script>
<style>.dark-theme{background:#222;color:#eee}.light-theme{background:#fff;color:#000}</style>

Theme changes automatically based on even/odd counter value.

Let’s Try →
9

Stimulus.js Full Featured Counter

<div data-controller="counter" data-counter-count-value="0" data-counter-dark-value="false" data-counter-step-value="2" data-counter-max-value="10">
    <h2>Counter: <span data-counter-target="count">0</span></h2>
    <div>History: <span data-counter-target="history"></span></div>
    <div>
        <button data-action="click->counter#increment">+</button>
        <button data-action="click->counter#decrement">-</button>
        <button data-action="click->counter#reset">Reset</button>
        <button data-action="click->counter#toggleTheme">Toggle Theme</button>
    </div>
</div>
<script>
    import { Application } from '@hotwired/stimulus';
    const application = Application.start();
    application.register('counter', class extends Stimulus.Controller {
        static targets = ['count','history'];
        static values = { count:Number,dark:Boolean,step:Number,max:Number };
        history=[];
        connect(){ this.interval=setInterval(()=>{ let c=this.countValue+this.stepValue; if(c>this.maxValue)c=0; this.countValue=c; this.history=[...this.history,'Auto Increment']; this.updateDisplay(); },1000); this.updateDisplay();}
        disconnectedCallback(){clearInterval(this.interval);}
        increment(){ this.countValue+=this.stepValue; this.history=[...this.history,'Increment']; this.updateDisplay(); }
        decrement(){ this.countValue-=this.stepValue; this.history=[...this.history,'Decrement']; this.updateDisplay(); }
        reset(){ this.countValue=0; this.history=[...this.history,'Reset']; this.updateDisplay(); }
        toggleTheme(){ this.darkValue=!this.darkValue; this.updateDisplay(); }
        updateDisplay(){ this.countTarget.textContent=this.countValue; this.historyTarget.textContent=this.history.join(', '); this.element.className=this.darkValue?'dark-theme':'light-theme';}
    });
</script>
<style>.dark-theme{background:#222;color:#eee}.light-theme{background:#fff;color:#000}</style>

Combines step increment, max limit, auto-reset, history, auto-increment, and theme toggle.

Let’s Try →

Frequently Asked Questions about Stimulus-js

What is Stimulus-js?

Stimulus.js is a modest JavaScript framework designed to enhance static HTML by connecting elements to JavaScript controllers. It complements server-rendered HTML, making it ideal for modestly interactive UIs without a heavy frontend framework.

What are the primary use cases for Stimulus-js?

Server-rendered apps with light JS enhancements. Rails and Turbo/Hotwire projects. Interactive form behaviors. Small UI widgets. Progressive enhancement without SPA complexity

What are the strengths of Stimulus-js?

Extremely lightweight and fast. Non-intrusive - enhances existing HTML. Great for progressive enhancement. Easy learning curve. Excellent integration with Rails/Hotwire

What are the limitations of Stimulus-js?

Not a full SPA framework. No built-in routing or state management. Limited for large-scale applications. Requires manual structuring for complex interactions. Smaller ecosystem compared to React/Vue

How can I practice Stimulus-js typing speed?

CodeSpeedTest offers 9+ real Stimulus-js code examples for typing practice. You can measure your WPM, track accuracy, and improve your coding speed with guided exercises.

Learn Other Programming Languages

CReactPythonC++RustTypeScriptKotlinPHPJavaC#RubyMqlCqlN1qlCypherGremlinPartiqlHaskellElixirFsharpView all languages →
CodeSpeedTest

Improve your coding speed, code accuracy, and programming syntax WPM with practice sessions across 500+ programming languages.

Quick Links

HomeAboutFeaturesGetting StartedLanguages

Legal & Support

Pro ⚡ PricingContactPrivacy PolicyTerms of Service

Connect

CodeSpeedTest on GitHubCodeSpeedTest on TwitterEmail CodeSpeedTest

© 2026 CodeSpeedTest. All rights reserved.