Stimulus JavaScript Framework

repository·main·Indexed 12 days ago

https://github.com/hotwired/stimulus

A modest JavaScript framework designed to augment existing HTML with behavior. Stimulus connects JavaScript controllers to HTML elements using data attributes (data-controller, data-target, and data-action), allowing developers to add behavior to server-rendered HTML without the complexity of a full SPA framework. Version 3.2.2.

Tokens
21.1K
Snippets
70
Records
99
Agent score
95%

What's inside Stimulus

  1. What is Stimulus and how does it work?

    main

    Stimulus is a JavaScript framework designed to enhance existing static or server-rendered HTML. Instead of managing the entire DOM, Stimulus connects JavaScript objects called controllers to HTML elements using data-controller attributes.

    Core Concepts

    Stimulus relies on four primary abstractions to bridge HTML and JavaScript:

    • Controllers: JavaScript classes that manage a specific piece of behavior. Stimulus instantiates a controller when it detects a data-controller attribute on an element.
    • Actions: Connect controller methods to DOM events using data-action attributes (e.g., connecting a click event to a method).
    • Targets: A way to locate and reference specific elements within a controller's scope.
    • Values: A mechanism to read, write, and observe data attributes on the controller's element, allowing data to flow between the HTML and the JavaScript logic.

    This approach separates content from behavior, similar to how CSS separates content from presentation.

  2. Share targets between multiple controllers

    main

    An HTML element can belong to multiple controllers by including multiple target attributes. This allows different controllers to interact with the same element.

    <form data-controller="search checkbox">
      <input type="checkbox" data-search-target="projects" data-checkbox-target="input">
      <input type="checkbox" data-search-target="messages" data-checkbox-target="input">
    </form>

    In this example:

    • The search controller can access this.projectsTarget and this.messagesTarget.
    • The checkbox controller can access this.inputTargets (an array containing both checkboxes).
  3. Understand the core concepts of Stimulus: Controllers, Actions, and Targets

    main

    Stimulus is a modest JavaScript framework designed to add behavior to existing HTML rather than creating it. It revolves around three main concepts that are declared directly in your HTML via data-* attributes, making the behavior readable as progressive enhancement:

    1. Controllers: Defined using data-controller="name". A controller is a JavaScript class that manages a specific piece of behavior.
    2. Actions: Defined using data-action="controller-name#method". These connect DOM events (like clicks) to specific methods within a controller.
    3. Targets: Defined using data-[controller-name]-target="name". These allow a controller to easily reference specific elements within its scope.

    By declaring these in the HTML, the template itself acts as a form of pseudocode that describes what the JavaScript is doing.

    <div data-controller="clipboard">
      PIN: <input data-clipboard-target="source" type="text" value="1234" readonly>
      <button data-action="clipboard#copy">Copy to Clipboard</button>
    </div>
  4. Define CSS classes by logical name in Stimulus

    main

    Instead of hard-coding CSS class strings in your JavaScript, you can define CSS classes by a logical name using the static classes array in your controller. This allows you to map logical names to specific CSS classes via HTML data attributes, making your controllers more reusable and decoupled from specific styling implementations.

    To define logical names, add a static classes array to your controller class containing the names in camelCase.

    // controllers/search_controller.js
    import { Controller } from "@hotwired/stimulus"
    
    export default class extends Controller {
      static classes = [ "loading" ]
    
      // …
    }
  5. Understand Controller Scopes and Nesting

    main

    Scopes

    When a controller connects to an element, that element and all its children constitute the controller's scope. The controller can only see targets and elements within this scope.

    Nested Scopes

    When controllers are nested, each controller is only aware of its own scope. It cannot see targets belonging to a nested controller.

    For example, if a list controller contains a nested list controller, the parent controller's targets will not include the targets defined inside the child controller's scope.

  6. Manage external resource lifecycles with connect and disconnect

    main

    When a controller manages external resources that are not part of the DOM (like setInterval timers or active HTTP requests), you must ensure they are properly cleaned up to prevent memory leaks or background processes running after the element is removed.

    • Acquisition: Use the connect() lifecycle callback to start timers or initiate requests.
    • Release: Use the disconnect() lifecycle callback to stop timers (e.g., via clearInterval) or cancel pending operations. This ensures the controller only consumes resources while it is actually connected to the DOM.
  7. How Stimulus differs from mainstream JavaScript frameworks

    main

    Stimulus follows a different paradigm than frameworks like React or Vue:

    • HTML vs. JSON: While mainstream frameworks focus on turning JSON into DOM elements via template languages, Stimulus attaches itself to existing HTML (usually rendered by the server). Its primary goal is to manipulate existing elements rather than create them.
    • State Management: In most frameworks, state is maintained in JavaScript objects and rendered to HTML. In Stimulus, state is stored in the HTML. This allows controllers to be discarded during page changes (like those performed by Turbo) and reinitialized correctly when the HTML is reloaded or cached.
    • Complexity: Stimulus is designed to avoid the heavy lifting and indirection of complex client-side state management, favoring the simplicity of the request-response paradigm.
  8. What are Outlets and how do they work?

    main

    Outlets allow one Stimulus controller (the host) to reference other Stimulus controller instances and their associated DOM elements using CSS selectors.

    Unlike Targets, which are scoped to the controller's own element, Outlets can be located anywhere on the page. They are primarily used for cross-controller communication and coordination, serving as an alternative to dispatching custom events.

    To use an outlet, the host controller must declare the identifier of the target controller in its static outlets array, and the HTML must provide a data-[identifier]-[outlet]-outlet attribute containing a CSS selector that matches the target controller's element.

    <!-- Host controller with an outlet pointing to '.online-user' -->
    <div data-controller="chat" data-chat-user-status-outlet=".online-user"></div>
    
    <!-- Target controller elements -->
    <div class="online-user" data-controller="user-status">...</div>
    <div class="online-user" data-controller="user-status">...</div>
  9. Use the connect() lifecycle method

    main

    The connect() method is a lifecycle callback that Stimulus automatically calls every time a controller is connected to the document (i.e., when the element with the data-controller attribute enters the DOM). This is useful for initializing logic or logging connection status.

    import { Controller } from "@hotwired/stimulus"
    
    export default class extends Controller {
      connect() {
        console.log("Controller connected!", this.element)
      }
    }
  10. Understand lifecycle method timing and execution

    main

    Stimulus uses the DOM MutationObserver API to watch for changes. Consequently, lifecycle methods are called asynchronously in the next microtask following a document change.

    Despite being asynchronous, methods follow a strict logical order:

    • Two connect() calls will always be separated by a disconnect() call.
    • Two [name]TargetConnected() calls for the same target will always be separated by a [name]TargetDisconnected() call for that same target.
  11. Use Stimulus lifecycle callbacks

    main

    Stimulus provides special methods called lifecycle callbacks that allow you to respond whenever a controller or its targets connect to or disconnect from the document. You can define these methods within your controller class to manage setup and teardown logic.

    import { Controller } from "@hotwired/stimulus"
    
    export default class extends Controller {
      connect() {
        // Called when the controller is connected to the DOM
      }
    
      disconnect() {
        // Called when the controller is disconnected from the DOM
      }
    }
  12. How Actions work in Stimulus

    main

    Actions are the mechanism used to handle DOM events within Stimulus controllers. An action creates a connection between a DOM event listener, the controller's element, and a specific controller method.

    An action descriptor follows the format: event->controller-identifier#method-name.

    Example:

    <div data-controller="gallery">
      <button data-action="click->gallery#next">…</button>
    </div>

    In the controller:

    import { Controller } from "@hotwired/stimulus"
    
    export default class extends Controller {
      next(event) {
        // logic to execute
      }
    }