> ## Documentation Index
> Fetch the complete documentation index at: https://sprincul.sinfullycoded.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sprincul Quick Start: Build Your First Reactive Component

> Learn how to build your first reactive component with Sprincul in three steps: define a model, register it, and annotate your HTML.

This guide walks you through building a reactive counter component from scratch. By the end, you will have a working example that demonstrates how Sprincul connects JavaScript state to HTML through data attributes — and you will understand the pattern well enough to apply it to your own components.

<Steps>
  <Step title="Define a model">
    Each component is a standard JavaScript class that extends `SprinculModel`. Initialize your reactive state on `this.state` inside `beforeInit()`, then add methods to update the DOM and handle events.

    The `beforeInit()` hook runs before Sprincul attaches any bindings, so it is the right place to set up initial state and computed properties. Any method you define on the class can be referenced directly from your HTML.

    ```js theme={null}
    // Counter.js
    import { SprinculModel } from 'sprincul';

    export default class Counter extends SprinculModel {
      beforeInit() {
        this.state.count = 0;
      }

      increment() {
        this.state.count++;
      }

      decrement() {
        this.state.count--;
      }

      /** @param {HTMLInputElement} el */
      showCount(el) {
        el.value = this.state.count;
      }

      resetCounter() {
        this.state.count = 0;
      }
    }
    ```

    `this.state.count` is reactive. Whenever its value changes, Sprincul automatically calls any DOM-bound callbacks that depend on it — in this case, `showCount`.
  </Step>

  <Step title="Register and initialize">
    Before calling `Sprincul.init()`, register every model class you want Sprincul to recognize. Use `register()` for a single model, or `registerAll()` to register several at once.

    ```js theme={null}
    // main.js — register a single model
    import { Sprincul } from 'sprincul';
    import Counter from './Counter.js';

    Sprincul.register('Counter', Counter);
    Sprincul.init();
    ```

    When you have multiple models, `registerAll()` keeps things tidy:

    ```js theme={null}
    // main.js — register multiple models at once
    import { Sprincul } from 'sprincul';
    import Counter from './Counter.js';
    import UserProfile from './UserProfile.js';
    import ShoppingCart from './ShoppingCart.js';

    Sprincul.registerAll({
      Counter,
      UserProfile,
      ShoppingCart
    });
    Sprincul.init();
    ```

    <Warning>
      All model registrations must happen **before** you call `Sprincul.init()`. After initialization runs, any models registered afterward will not be picked up until you call `Sprincul.init()` again.
    </Warning>

    When `Sprincul.init()` runs, it scans the DOM for every element with a `data-model` attribute, creates an instance of the matching class, wires up your `data-bind-*` bindings and event listeners, and then runs the lifecycle hooks (`beforeInit` followed by `afterInit`).
  </Step>

  <Step title="Annotate your HTML">
    Wrap each model in a container element with a `data-model` attribute set to the name you registered. Place your bindings and event attributes anywhere inside that container.

    ```html theme={null}
    <div data-model="Counter">
      <p>Counter</p>
      <button onclick="decrement">-</button>
      <input type="number" data-bind-count="showCount" readonly />
      <button onclick="increment">+</button>
      <button type="reset" onclick="resetCounter">reset</button>
    </div>
    ```

    Here is how each attribute type works:

    * `data-model="Counter"` — marks this element as the root of a Counter model instance. Sprincul creates one class instance per root element.
    * `data-bind-count="showCount"` — whenever `this.state.count` changes, Sprincul calls `showCount(element)` with the bound element as the argument, letting your method update it however you like.
    * `onclick="decrement"` — Sprincul converts this into a proper `addEventListener('click', ...)` call on your model instance, then removes the inline attribute. The native `Event` object is passed to the handler.

    <Tip>
      State property names used in `data-bind-*` attributes must be lowercase. Browsers normalize `data-*` attribute names to lowercase when building the DOM, so `data-bind-count` maps to `this.state.count`, not `this.state.Count`.
    </Tip>
  </Step>
</Steps>

That is all it takes. Load your `main.js` as a module, open the page, and the counter is live:

```html theme={null}
<script type="module" src="./main.js"></script>
```
