Files
michaelschiemer/docs/livecomponents/01-getting-started.md
Michael Schiemer fc3d7e6357 feat(Production): Complete production deployment infrastructure
- 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.
2025-10-25 19:18:37 +02:00

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

  1. Start the development server:

    make up
    npm run dev
    
  2. Visit the demo page:

    https://localhost/demo
    
  3. 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 → input event → Send to server → Update $name property → 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

Troubleshooting

Component Not Responding

Check:

  1. Is data-component-id present in the rendered HTML?
  2. Is the JavaScript bundle loaded? (Check browser console)
  3. Are there any JavaScript errors? (F12 → Console)
  4. 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