- Add comprehensive health check system with multiple endpoints - Add Prometheus metrics endpoint - Add production logging configurations (5 strategies) - Add complete deployment documentation suite: * QUICKSTART.md - 30-minute deployment guide * DEPLOYMENT_CHECKLIST.md - Printable verification checklist * DEPLOYMENT_WORKFLOW.md - Complete deployment lifecycle * PRODUCTION_DEPLOYMENT.md - Comprehensive technical reference * production-logging.md - Logging configuration guide * ANSIBLE_DEPLOYMENT.md - Infrastructure as Code automation * README.md - Navigation hub * DEPLOYMENT_SUMMARY.md - Executive summary - Add deployment scripts and automation - Add DEPLOYMENT_PLAN.md - Concrete plan for immediate deployment - Update README with production-ready features All production infrastructure is now complete and ready for deployment.
12 KiB
Getting Started with LiveComponents
This guide will help you set up and create your first LiveComponent.
Prerequisites
- PHP 8.4+ (8.5 recommended)
- Custom PHP Framework installed
- Node.js 18+ for frontend assets
- Docker (optional, for development environment)
Installation
LiveComponents is built into the Custom PHP Framework. No additional installation is required.
Verify Installation
# Check if LiveComponents are available
docker exec php php console.php list | grep livecomponent
# Should show LiveComponent-related commands
Project Structure
src/
├── Application/
│ └── LiveComponents/ # Your LiveComponent classes
├── Framework/
│ └── LiveComponents/ # Framework LiveComponents code
│ ├── LiveComponent.php # Base class
│ ├── Attributes/ # Attributes (#[LiveProp], etc.)
│ └── Services/ # Internal services
resources/
├── js/
│ └── modules/
│ └── LiveComponent.js # Client-side JavaScript
└── views/
└── components/ # Component templates
public/
└── assets/
└── js/
└── livecomponent.js # Compiled JavaScript
Configuration
Environment Variables
Add to your .env file:
# LiveComponents Configuration
LIVECOMPONENT_ENABLED=true
LIVECOMPONENT_CSRF_PROTECTION=true
LIVECOMPONENT_RATE_LIMIT=60 # Requests per minute
LIVECOMPONENT_BATCH_SIZE=10 # Max actions per batch
LIVECOMPONENT_BATCH_DEBOUNCE=50 # Debounce delay in ms
LIVECOMPONENT_SSE_ENABLED=true
LIVECOMPONENT_SSE_HEARTBEAT=15 # Heartbeat interval in seconds
LIVECOMPONENT_DEVTOOLS_ENABLED=false # Enable in development only
Frontend Assets
Ensure LiveComponent JavaScript is included in your base layout:
<!-- resources/views/layouts/app.view.php -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="csrf-token" content="{csrf_token}">
<title>{title}</title>
</head>
<body>
<slot>Main content</slot>
<!-- LiveComponent JavaScript (built by Vite) -->
<script type="module" src="/assets/js/main.js"></script>
</body>
</html>
The LiveComponent module is automatically loaded via the main JavaScript entry point.
Creating Your First Component
Step 1: Create Component Class
Create a new file: src/Application/LiveComponents/HelloWorld.php
<?php
declare(strict_types=1);
namespace App\Application\LiveComponents;
use App\Framework\LiveComponents\LiveComponent;
use App\Framework\LiveComponents\Attributes\LiveAction;
use App\Framework\LiveComponents\Attributes\LiveProp;
final class HelloWorld extends LiveComponent
{
#[LiveProp]
public string $name = 'World';
#[LiveProp]
public int $clickCount = 0;
#[LiveAction]
public function updateName(string $newName): void
{
$this->name = $newName;
}
#[LiveAction]
public function incrementClicks(): void
{
$this->clickCount++;
}
public function render(): string
{
return $this->view('components/hello-world', [
'name' => $this->name,
'clickCount' => $this->clickCount,
'greeting' => $this->getGreeting()
]);
}
private function getGreeting(): string
{
return "Hello, {$this->name}!";
}
}
Step 2: Create Component Template
Create template: resources/views/components/hello-world.view.php
<div
data-component-id="{component_id}"
data-component-name="HelloWorld"
class="hello-world-component"
>
<h2>{greeting}</h2>
<div class="input-group">
<label for="name-input">Your Name:</label>
<input
type="text"
id="name-input"
data-lc-model="name"
value="{name}"
placeholder="Enter your name"
/>
</div>
<div class="click-counter">
<p>Button clicked: <strong>{clickCount}</strong> times</p>
<button data-lc-action="incrementClicks" class="btn btn-primary">
Click Me!
</button>
</div>
<style>
.hello-world-component {
padding: 20px;
border: 1px solid #ccc;
border-radius: 8px;
max-width: 400px;
}
.input-group {
margin: 15px 0;
}
.input-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.input-group input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
}
.click-counter {
margin-top: 20px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.btn-primary {
background: #007bff;
color: white;
}
.btn-primary:hover {
background: #0056b3;
}
</style>
</div>
Step 3: Use Component in Controller
<?php
namespace App\Application;
use App\Framework\Attributes\Route;
use App\Framework\Http\Method;
use App\Framework\Http\ViewResult;
use App\Framework\LiveComponents\LiveComponent;
use App\Application\LiveComponents\HelloWorld;
final readonly class HomeController
{
#[Route(path: '/demo', method: Method::GET)]
public function demo(): ViewResult
{
return new ViewResult('demo', [
'title' => 'LiveComponent Demo',
'helloComponent' => LiveComponent::mount(HelloWorld::class)
]);
}
}
Step 4: Create View Template
Create: resources/views/demo.view.php
<include template="layouts/app" data="{title: title}">
<div class="container">
<h1>LiveComponent Demo</h1>
<p>This component is fully interactive without writing any JavaScript!</p>
{helloComponent}
</div>
</include>
Step 5: Test Your Component
-
Start the development server:
make up npm run dev -
Visit the demo page:
https://localhost/demo -
Try the interactions:
- Type in the name input → Greeting updates automatically
- Click the button → Counter increments
- Open DevTools (F12) → Check Network tab for LiveComponent requests
How It Works
1. Component Mounting
When the page loads:
LiveComponent::mount(HelloWorld::class)
This creates a component instance and renders the initial HTML with a unique component_id.
2. Client-Side Initialization
The JavaScript module detects components by data-component-id:
// Automatic initialization
document.querySelectorAll('[data-component-id]').forEach(element => {
LiveComponent.initializeComponent(element);
});
3. Two-Way Data Binding
Input with data-lc-model="name":
- User types →
inputevent → Send to server → Update$nameproperty → Re-render → Update DOM
4. Action Execution
Button with data-lc-action="incrementClicks":
- User clicks → Send action to server → Execute
incrementClicks()→ Re-render → Update DOM
5. Request Flow
Client Server
│ │
├─ Click Button ──────────────>│
│ ├─ Find Component
│ ├─ Execute Action
│ ├─ Render Template
│<─ JSON Response ─────────────┤
├─ Update DOM │
│ │
Response Format:
{
"success": true,
"html": "<div data-component-id=\"abc123\">...</div>",
"fragments": {
"results": "<div>Updated fragment HTML</div>"
},
"redirect": null
}
Common Data Binding Patterns
Input Fields
<!-- Text input -->
<input type="text" data-lc-model="title" value="{title}" />
<!-- Textarea -->
<textarea data-lc-model="description">{description}</textarea>
<!-- Checkbox -->
<input type="checkbox" data-lc-model="agreed" {agreed ? 'checked' : ''} />
<!-- Select -->
<select data-lc-model="category">
<option value="tech" {category === 'tech' ? 'selected' : ''}>Tech</option>
<option value="news" {category === 'news' ? 'selected' : ''}>News</option>
</select>
Actions with Parameters
<!-- Simple action -->
<button data-lc-action="save">Save</button>
<!-- Action with inline parameters -->
<button
data-lc-action="updateStatus"
data-lc-params='{"status": "published"}'
>
Publish
</button>
<!-- Action from event handler -->
<form data-lc-submit="handleSubmit">
<input type="text" name="email" />
<button type="submit">Submit</button>
</form>
Optimistic Updates
<!-- Instant UI feedback -->
<button
data-lc-action="toggleLike"
data-optimistic="true"
>
{isLiked ? '❤️' : '🤍'} Like ({likeCount})
</button>
Debugging
Enable DevTools Overlay
Set in .env:
LIVECOMPONENT_DEVTOOLS_ENABLED=true
Or activate via localStorage:
localStorage.setItem('livecomponent_devtools', 'true');
location.reload();
DevTools Features
- Component Tree: Inspect all components on the page
- Action Log: See all executed actions with timing
- Event Log: Track client and server events
- Performance: Flamegraphs, timeline, memory profiling
- Network: Monitor LiveComponent HTTP requests
- DOM Badges: Visual component boundaries with activity
Console Debugging
// Get component instance
const component = LiveComponent.getComponent('component-id-here');
// Inspect component state
console.log(component.state);
// Manually trigger action
LiveComponent.executeAction('component-id', 'actionName', { param: 'value' });
// Monitor events
window.addEventListener('livecomponent:action-executed', (e) => {
console.log('Action executed:', e.detail);
});
Next Steps
- Your First Component - Detailed walkthrough
- Core Concepts - Deep dive into architecture
- Security Guide - CSRF, validation, rate limiting
- Performance Guide - Optimization techniques
Troubleshooting
Component Not Responding
Check:
- Is
data-component-idpresent in the rendered HTML? - Is the JavaScript bundle loaded? (Check browser console)
- Are there any JavaScript errors? (F12 → Console)
- Is HTTPS enabled? (Required for some features)
Solution:
# Rebuild JavaScript assets
npm run build
# Check server logs
docker logs php
# Verify component registration
docker exec php php console.php livecomponent:list
CSRF Token Errors
Error: "CSRF token mismatch"
Solution:
<!-- Ensure CSRF meta tag is present -->
<meta name="csrf-token" content="{csrf_token}">
Rate Limiting
Error: "Too many requests"
Solution: Increase rate limit in .env:
LIVECOMPONENT_RATE_LIMIT=120 # Increase to 120 per minute
Common Pitfalls
❌ Forgetting data-component-id
<!-- Wrong -->
<div class="component">...</div>
<!-- Correct -->
<div data-component-id="{component_id}">...</div>
❌ Not using #[LiveProp] attribute
// Wrong - won't sync with client
public string $name = '';
// Correct
#[LiveProp]
public string $name = '';
❌ Mutating props directly in template
<!-- Wrong - props are readonly on client -->
<button onclick="name = 'new value'">Change</button>
<!-- Correct - use actions -->
<button data-lc-action="updateName" data-lc-params='{"name": "new value"}'>
Change
</button>
Next: Your First Component Tutorial →