Skip to content
Interact for CakePHP

Meet CakePHP Interact

Reactive interfaces. The CakePHP way.

Build interactive CakePHP interfaces with PHP and Alpine. Add live search, responsive forms, and smooth page visits without leaving the stack you know.

CakePHP 6.x (unreleased) · PHP 8.5+

Add to existing CakePHP pages No JavaScript build required

A little PHP. A live interface. Live PHP
<?php
class ExoplanetsIsland extends Island
{
    #[State(writable: true)]
    public string $search = '';

    #[Pagination(url: true, history: ['page'])]
    public PaginationState $paging;

    public function viewData(): array
    {
        $planets = $this->fetchTable(ExoplanetsTable::class);
        $query = $planets->find('search', search: $this->search);

        return ['planets' => $this->paginate($query, ['limit' => 2])];
    }
}
Simplified example · imports omitted

Explore distant worlds.

Try it here

300 planets in the collection

PHP → HTML
  • WASP-15 b

    Transit

    Discovered in 2009

  • WASP-17 b

    Transit

    Discovered in 2009

Planet data from the NASA Exoplanet Archive.

How it works

PHP and Alpine, working together.

Define behavior in PHP, express interactions with Alpine, and let Interact keep state and rendered HTML in sync.

Try Interact

A little mission control.

Try each interaction, then take a peek at the code that makes it work.

Live binding

Name your spacecraft and see its call sign update.

Call sign: awaiting a name.

<label for="demo-name">Spacecraft name</label>
<input id="demo-name" name="name"
    x-interact:model.live>

PHP actions

Pack supplies with an action that runs in PHP.

0supplies packed
<?php
#[Action]
public function increment(): void
{
    $this->count++;
}

Loading feedback

Run a simulated system check and see when it is working.

Simulated check · 450ms

<button :disabled="$server.busy">
    <span x-show="$server.busy">Working…</span>
</button>

URL state

Choose your display spacing. The URL remembers it.

ScanMapReport
<?php
#[State(writable: true, url: true, history: true)]
public string $density = 'comfortable';

Polling

Keep the mission clock fresh while this island is visible.

Mission clock · UTC · Updates every 5 seconds

<section x-interact:poll.5s.visible>
    <!-- Fresh PHP output, just here. -->
</section>

Alpine state

Open mission details instantly with local browser state.

Destination: the stars. This detail opens locally.

<div x-data="{ expanded: false }">
    <button @click="expanded = !expanded">
        View mission details
    </button>
</div>

Image uploads

Choose a snapshot. Interact uploads it, and Glide gives it a cosmic finish.

JPEG, PNG or WebP, up to 2 MiB. Click the preview to choose a different image.

Click to upload an image

JPEG, PNG or WebP · up to 2 MiB

<?php
// src/Island/ImageUploadIsland.php
class ImageUploadIsland extends Island
{
    #[Upload(maxFileSize: '2MB')]
    public ?UploadedFileInterface $image = null;
}

// templates/island/image_upload.php
?>
<input type="file" name="image" x-interact:model>
<?php if ($image !== null): ?>
    <?= $this->Glide->image($thumbnailPath, ['p' => 'space']) ?>
<?php endif; ?>

Simplified: validation and Glide configuration omitted.

Events

Send a signal from one island and watch another respond.

Spacecraft
Mission control 0 signals received
<?php
// In the sending island:
#[Action]
public function send(): void
{
    $this->dispatch('mission-signal');
}

// In the receiving island:
#[Action(on: 'mission-signal')]
public function receive(): void
{
    $this->received++;
}

Two independent islands, connected by an event.

Streaming

Watch a simulated mission report arrive in stages from one PHP action.

Ready when you are.

    

Simulated report · One request, three updates.

<?php
#[Action(stream: true)]
public function generate(): void
{
    $line = 'Scanning the horizon.';
    $this->stream('report', $line . "\n");
}

More with Interact

Keep going. Keep your stack.

There is more to explore when your application needs it.

Realtime across tabs

Refresh subscribed islands or receive events from other requests and jobs. Bring a provider adapter, including the optional Mercure integration.

Realtime subscriptions

Keep the page visits flowing

Use real links with enhanced navigation. Titles, history, scroll, and focus follow the visit without a full browser reload.

Explore navigation

Pagination, already connected

Pass your ORM query to paginate(). The Paginator helper produces real links that update the island and keep its filters in the URL.

Paginate an island

Compose smaller islands

Nest independent islands, pass reactive props, bind child values, and coordinate through events. Load them lazily when useful.

Compose islands Explore lazy loading

Uploads that stay in the flow

Send files with your island action and show upload progress. Keep validation, storage, and permissions in your CakePHP application.

Handle uploads

Build one

Your first island.

A PHP class, a template, and one render call. Once you have installed Interact, these three pieces make a working counter.

  1. Define an island

    Declare the count as state and expose a PHP action to change it.

    src/Island/CounterIsland.php

  2. Write its template

    Use ordinary HTML and Alpine to call the action and show the result.

    templates/island/counter.php

  3. Render it in a page

    Add the island to an existing view. Your controller keeps its normal action.

    An existing view template

Follow the quickstart
<?php
declare(strict_types=1);

namespace App\Island;

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

/** Demonstrates server-owned state changed through an explicit PHP action. */
class CounterIsland extends Island
{
    #[State]
    public int $count = 0;

    /** Increment this island without touching the other examples. */
    #[Action]
    public function increment(): void
    {
        $this->count++;
    }
}
Counter island · plain example templates

Start with one interaction.

A search box, a live preview, or an inline editor. Add Interact where your application needs it.

Get started

Requires PHP 8.5+ and CakePHP 6.x, currently unreleased.