Skip to content

Quickstart ​

Build a counter that updates without reloading its surrounding page. You will add a PHP class, a template, and one mount call to an existing controller view.

Complete installation first: load the plugin and helper, build and serve the browser runtime, output the layout's script block, and keep CSRF middleware enabled. No database or custom JavaScript is needed for this example.

Declare an island ​

Create src/Island/CounterIsland.php. Interact resolves Counter to this class when the helper renders it. No registration or discovery attribute is required.

php
<?php
declare(strict_types=1);

namespace App\Island;

use Interact\Attribute\Action;
use Interact\Attribute\State;
use Interact\Island;

/**
 * Keeps a count for one region and changes it through a server action.
 *
 * Save as src/Island/CounterIsland.php.
 */
class CounterIsland extends Island
{
    #[State]
    public int $count = 0;

    /**
     * Increase this island's count when its button is pressed.
     */
    #[Action]
    public function increment(): void
    {
        $this->count++;
    }
}

State keeps $count between requests. Without writable: true, the browser cannot assign it directly. Action exposes increment() as an intentional way to change that value.

Write the template ​

The inferred template receives $count on the initial render and every update:

php
<?php
declare(strict_types=1);

/**
 * templates/island/counter.php
 *
 * @var int $count
 */
?>
<p aria-live="polite">Count: <?= (string)$count ?></p>
<button
    type="button"
    @click="$server.call('increment')"
    :disabled="$server.busy"
>
    Add one
</button>
<p role="alert" x-text="$server.error ? $server.error.message : ''"></p>

@click invokes the PHP action through $server.call(). The Alpine shorthand :disabled="$server.busy" disables the button while a request is running. The alert displays a request failure when one occurs.

Mount it in an existing page ​

In a template already rendered by your application, add:

php
<?php
declare(strict_types=1);

/**
 * templates/Articles/index.php
 *
 * @var \Cake\View\View $this
 */
?>
<h1>Articles</h1>
<?= $this->Interact->render('Counter') ?>

Counter resolves to App\Island\CounterIsland. Its default template is templates/island/counter.php; underscored names such as article_editor resolve to ArticleEditorIsland.

Open your existing articles page. You should see Count: 0. Press Add one: the count becomes 1, and the heading and surrounding layout stay in place. Each click sends a POST to the update endpoint. If the count does not change, follow the troubleshooting checks.

Your controller needs no AJAX branch. It still renders its normal action template on the first request; later requests go directly to Interact's update endpoint.

Add an independent counter ​

Render the same island name again with distinct stable keys:

php
<?= $this->Interact->render('Counter', key: 'first') ?>
<?= $this->Interact->render('Counter', key: 'second') ?>

Each starts at zero and updates independently. Reloading the page starts fresh: the count is interaction state, and this example does not save it to a database.

See Conventions for organizing related islands into matching class and template folders.

Choose your next step ​

TaskGuide
Look up a directiveHTML directives
Look up a JavaScript bridge member$server
Bind an input or pass an initial valueProperties
Read PHP data from Alpine or a JavaScript fileAlpine and $server
Call an action with arguments or show loading feedbackActions
Upload and validate filesFile uploads
Filter a larger model-backed listingSearch and pagination
Validate and save an editable recordArticle form
Reuse a controller action's templatePage islands

Application-owned Alpine ​

If your app already starts Alpine, disable the helper's standalone script and register Interact with that instance. Follow the application-owned Alpine setup.

Reuse an action template ​

An island can own the content of an action template while its normal layout stays in place. Follow whole-page islands for the cooperating island, controller, and template.