# Quick start

Start with signals, grow into a component, end as a native HTML tag. Each step below adds one layer on top of the previous — the reactive core never changes, only how you package it.

## From signal to custom element

```tsx
// 5. Custom element. The same counter, usable as <x-counter initial="3" />.
import { reactive, computed } from "elements-kit/signals";
import { attributes, ATTRIBUTES as attr } from "elements-kit/attributes";
import { render } from "elements-kit/render";

@attributes
class CounterElement extends HTMLElement {
  static [attr] = {
    initial(this: CounterElement, v: string | null) {
      this.count = Number(v ?? 0);
    },
  };

  @reactive() count = 0;
  doubled = computed(() => this.count * 2);

  #unmount?: () => void;

  #template = () => (
      <section>
        <p>
          <strong>{() => this.count}</strong> × 2 = <strong>{this.doubled}</strong>
        </p>
        <button on:click={() => this.count++}>+1</button>
      </section>
  );

  connectedCallback() {
    this.#unmount = render(this, this.#template);
  }

  disconnectedCallback() {
    this.#unmount?.();
    this.#unmount = undefined;
  }
}

customElements.define("x-counter", CounterElement);
```

## What's happening

* **Step 1 — signals.** `signal(0)` holds state; `computed(() => count() * 2)` derives from it. Both are framework-agnostic — no DOM involved.
* **Step 2 — signals + JSX.** `render(target, setup)` runs `setup` inside an [`effectScope`](/scopes), appends the returned node to `target`, and hands back a single `unmount` thunk. `{count}` creates a live text node that updates in place — no diffing, no re-render.
* **Step 3 — function component.** Props are typed with `Props<T>`; each key on `props` arrives as a `Computed<T>` (callable getter). Return type is `Element` — a real DOM node.
* **Step 4 — class component.** `@reactive()` turns class fields into signals with natural property access. `render(): Element` returns the same shape, called once when the instance mounts.
* **Step 5 — custom element.** `@attributes` wires the `static [attr]` map so HTML attributes feed reactive properties. `connectedCallback` mounts; `disconnectedCallback` tears down. Usable as a plain HTML tag in any framework or no framework at all.

The reactive core (steps 1–2) is doing all the work. Steps 3–5 are just packaging.

## Next steps

* Learn the signal primitives in depth → [Signals](/signals).
* Understand JSX → DOM compilation → [Elements](/elements).
* Wrap the same pattern in a native custom element → [Custom elements](/custom-elements).

## See also

* [Installation](/getting-started/installation)
* [Philosophy](/getting-started/philosophy)
* [Scopes](/scopes)
