Skip to content

Alpine ​

Interact includes Alpine.js for browser-side behavior inside islands. Use Alpine for local UI state and $server for PHP state and actions.

Template expressions ​

html
<div x-data="{ expanded: false }">
  <button type="button" @click="expanded = !expanded">Toggle details</button>
  <p x-show="expanded" x-text="'Count: ' + $server.state.count"></p>
</div>

This assumes an island with a declared count property. expanded is local; changing it sends no request. $server.state.count reads the current island value.

Props and state are browser-visible. viewData() supplies PHP-only variables; see Properties. Copying a value into x-data, such as { initialCount: $server.state.count }, initializes a local value once. Reading $server.state.count directly remains reactive.

CSP build ​

The Composer bundle uses Alpine's CSP build. Object literals, property access, arithmetic, ternaries, assignments, and method calls work in attributes without unsafe-eval. Inline method definitions and arrow callbacks are not supported; put them in an external Alpine.data() registration.

External files use normal JavaScript syntax. Applications owning their Alpine instance can choose standard Alpine instead, subject to its unsafe-eval requirement. See installation.

Custom JavaScript ​

The Composer runtime exposes window.Alpine. Register behavior in a plain external script before the runtime starts:

js
// webroot/js/counter-tools.js
document.addEventListener('alpine:init', () => {
  window.Alpine.data('counterTools', () => ({
    increment() {
      return this.$server.call('increment');
    },
  }));
});

Load it before the layout's Interact script block, with defer:

php
<?= $this->Html->script('counter-tools', ['defer' => true]) ?>
<?= $this->fetch('script') ?>

Attach it inside the counter island:

html
<div x-data="counterTools">
  <button type="button" @click="increment()">Add one</button>
</div>

Inside methods, use this.$server. In template expressions, use $server. A registration can be reusable; each instance resolves to its nearest containing island. Outside an island, $server is unavailable.

Keep automatic script loading enabled for this setup. Adding application JavaScript does not require rebuilding Interact or importing another Alpine instance. The Quickstart counter provides increment().

Watching state and cleaning up ​

Use init() for initialization and $watch() for accepted state changes. Use destroy() to release library instances and resources. Keep library instances outside reactive state, and return promises from request methods.

The enhanced select and chart examples include complete registrations and lifecycle handling.

Application-owned Alpine ​

If an application already starts Alpine, install Interact on that instance and register application objects before the existing Alpine.start(). Disable Interact's automatic script for this integration. The complete setup is in Installation.