Server-driven SPA architecture for CakePHP 5. Build reactive, single-page applications without JavaScript frameworks.
Based on CakePHP Plugin Template.
- SPA Navigation - Load pages via AJAX with History API support
- Reactive Components - Livewire-like reactivity without writing JavaScript
- JSON/HTML Hybrid - Seamless handling of both response types
- CSRF Compatible - Full security integration with CakePHP
- Zero Configuration - Works out of the box with sensible defaults
- Framework Agnostic - No JavaScript framework dependencies
- PHP 8.1+
- CakePHP 5.0+
You can install this plugin directly from GitHub using Composer:
-
Add the GitHub repository to your app's
composer.json:"repositories": [ { "type": "vcs", "url": "https://github.com/CakePHPMitra/spa" } ]
-
Require the plugin via Composer:
composer require cakephpmitra/spa:dev-master
-
Load the plugin:
Method 1: from terminal
bin/cake plugin load CakeSPA
Method 2: load in
Application.php, bootstrap methodpublic function bootstrap(): void { parent::bootstrap(); $this->addPlugin('CakeSPA'); }
## Quick Start
### 1. Add the Component to Your Controller
```php
// src/Controller/AppController.php
public function initialize(): void
{
parent::initialize();
$this->loadComponent('CakeSPA.Spa');
}
// templates/layout/default.php
<!DOCTYPE html>
<html>
<head>
<?= $this->Spa->meta() ?>
<?= $this->Spa->scripts() ?>
</head>
<body>
<nav>
<?= $this->Spa->navLink('Home', '/') ?>
<?= $this->Spa->navLink('About', '/about') ?>
</nav>
<?= $this->Spa->contentContainer($this->fetch('content')) ?>
</body>
</html>// src/Controller/CounterController.php
class CounterController extends AppController
{
public function index()
{
$count = $this->request->getSession()->read('count', 0);
$this->set(compact('count'));
}
public function increment()
{
$count = $this->request->getSession()->read('count', 0) + 1;
$this->request->getSession()->write('count', $count);
return $this->Spa->respond(['count' => $count]);
}
}// templates/Counter/index.php
<div>
<p>Count: <?= $this->Spa->target('count', $count) ?></p>
<?= $this->Spa->button('Increment', 'counter/increment') ?>
</div>See the docs folder for detailed documentation:
- Installation Guide
- Features - Usage and examples
- Development - Configuration and security
When deploying your CakePHP app under a subdirectory/alias (e.g., https://example.com/myapp/), CakeSPA automatically handles URL resolution.
Include meta() in your layout's <head> section:
<?= $this->Spa->meta() ?>This generates both CSRF and base URL meta tags. The JavaScript automatically detects the base URL using a fallback chain:
<meta name="base-url">(recommended - server-generated)<base href>tag- Script src detection
- Origin fallback
No additional configuration required - navigation links and AJAX requests automatically use the correct base path.
CakeSPA uses a simple data-attribute based approach:
- Actions: Elements with
data-spa-actiontrigger AJAX requests - Targets: Elements with
data-spa-modelauto-update from JSON responses - Navigation: Links with
data-spa-navload pages without full reload
The server returns JSON for component updates and HTML for page navigation, determined automatically by request headers.
If you're migrating from the original CakeLive implementation:
| CakeLive | CakeSPA |
|---|---|
$this->Live->button() |
$this->Spa->button() |
$this->Live->target() |
$this->Spa->target() |
data-live-action |
data-spa-action |
data-live-model |
data-spa-model |
LiveComponentTrait |
SpaComponent |
See CONTRIBUTING.md for guidelines.
Report bugs and feature requests on the Issue Tracker.
MIT License. See LICENSE for details.