# Alpine.js Magics & Globals Reference

## Magics (available in expressions)

| Magic                              | Purpose                                                      |
| ---------------------------------- | ------------------------------------------------------------ |
| `$el`                              | Current DOM element                                          |
| `$refs.name`                       | Element marked with `x-ref="name"`                           |
| `$store.name`                      | Access global store registered via `Alpine.store()`          |
| `$watch('prop', (val, old) => {})` | Watch a property for changes                                 |
| `$dispatch('event', { data })`     | Dispatch custom DOM event (bubbles up)                       |
| `$nextTick(() => {})`              | Run after Alpine finishes DOM updates (also returns Promise) |
| `$root`                            | Root element of the current `x-data` scope                   |
| `$data`                            | Current scope's data object                                  |
| `$id('name')`                      | Generate scoped unique ID (requires `x-id`)                  |
| `$event`                           | Native event object inside `@` handlers                      |

### $dispatch patterns

```html
<!-- Sibling/ancestor communication (events bubble) -->
<div @notify="alert($event.detail.message)">
  <button @click="$dispatch('notify', { message: 'Hello' })">
</div>

<!-- Cross-component: use .window modifier on listener -->
<span @notify.window="...">
```

### $watch caveats

- Supports dot notation: `$watch('foo.bar', callback)`
- Deep watches return the full object, not the changed sub-property
- Never mutate the watched property inside its callback (infinite loop)

## Globals (register before Alpine.start())

### Alpine.data() — reusable components

```javascript
Alpine.data('dropdown', (initialOpen = false) => ({
  open: initialOpen,
  init() { /* runs before element init */ },
  destroy() { /* runs when element removed */ },
  toggle() { this.open = !this.open },
  get isOpen() { return this.open },
}))
```

```html
<div x-data="dropdown(true)">
  <button @click="toggle()" x-text="isOpen ? 'Close' : 'Open'"></button>
</div>
```

Access magics via `this` inside methods: `this.$watch(...)`, `this.$dispatch(...)`, `this.$el`.

### Alpine.store() — global reactive state

```javascript
Alpine.store('darkMode', {
  on: false,
  init() { this.on = matchMedia('(prefers-color-scheme:dark)').matches },
  toggle() { this.on = !this.on },
})

// Single-value store
Alpine.store('count', 0)
```

```html
<button @click="$store.darkMode.toggle()">Toggle</button>
<div :class="$store.darkMode.on && 'dark'">
```

### Alpine.bind() — reusable directive bundles

```javascript
Alpine.bind('SortableItem', () => ({
  'x-sort:item'() { return true },
  '@click'() { this.select() },
  ':class'() { return { active: this.selected } },
}))
```

```html
<li x-bind="SortableItem">...</li>
```

## Lifecycle Events

| Event                | When                                                           |
| -------------------- | -------------------------------------------------------------- |
| `alpine:init`        | After Alpine loads, before DOM init — register extensions here |
| `alpine:initialized` | After Alpine finishes initializing the page                    |
