Files
michaelschiemer/docs/livecomponents/faq.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

18 KiB

LiveComponents FAQ

Frequently Asked Questions about LiveComponents


General Questions

What are LiveComponents?

LiveComponents is a server-side component system that enables building interactive web applications with minimal JavaScript. Components render on the server, sync state automatically, and update the DOM intelligently via fragment-based rendering.

Key Benefits:

  • Write interactive UIs in PHP
  • Automatic state synchronization
  • Fragment-based partial updates
  • Built-in security (CSRF, rate limiting, validation)
  • Real-time updates via Server-Sent Events

When should I use LiveComponents vs traditional JavaScript?

Use LiveComponents for:

  • Admin dashboards
  • CRUD interfaces
  • Forms with complex validation
  • Real-time data displays
  • Prototyping interactive features

Use traditional JavaScript for:

  • Complex client-side logic
  • Heavy animations
  • Offline-first applications
  • Games or interactive visualizations
  • High-frequency updates (>60fps)

Use Both:

  • LiveComponents for server-side state management
  • JavaScript for client-side enhancements

How do LiveComponents compare to Livewire/Phoenix LiveView?

Similarities:

  • Server-side rendering
  • Automatic state sync
  • Minimal JavaScript required

Differences:

  • Custom Framework Integration: Built specifically for this framework's patterns (readonly, composition, value objects)
  • Fragment Rendering: More granular DOM updates
  • Request Batching: Automatic batching with configurable debouncing
  • PHP 8.5 Optimized: Leverages latest PHP performance improvements

Installation & Setup

How do I install LiveComponents?

LiveComponents is built into the framework - no additional installation needed.

Verify installation:

# Check if LiveComponent JavaScript is compiled
ls public/assets/js/livecomponent.js

# Verify base class exists
grep -r "abstract class LiveComponent" src/Framework/LiveComponents/

What are the minimum requirements?

Server Requirements:

  • PHP 8.4+ (8.5 recommended)
  • Composer
  • Custom PHP Framework installed

Browser Requirements:

  • Chrome/Edge 90+
  • Firefox 88+
  • Safari 14+
  • ES2020 JavaScript support
  • Fetch API
  • EventSource (SSE)

How do I enable HTTPS for development?

HTTPS is required for LiveComponents (CSRF protection, SSE).

Framework includes SSL certificates for local development:

# Start development server with HTTPS
make up
npm run dev

# Access via HTTPS only
https://localhost

Component Development

Can I use TypeScript with LiveComponents?

Yes! Add type definitions:

// livecomponents.d.ts
declare global {
    interface Window {
        LiveComponent: {
            mount(element: HTMLElement): void;
            executeAction(
                componentId: string,
                action: string,
                params?: object
            ): Promise<void>;
            getComponent(componentId: string): Component | null;
        };
    }
}

export {};

How do I pass initial data to a component?

// In controller
use App\Framework\LiveComponents\LiveComponent;

public function dashboard(): ViewResult
{
    return new ViewResult('dashboard', [
        'statsWidget' => LiveComponent::mount(StatsWidget::class, [
            'user_id' => $currentUser->id,
            'date_range' => 'last_30_days'
        ])
    ]);
}

// In component
final class StatsWidget extends LiveComponent
{
    #[LiveProp]
    public string $userId;

    #[LiveProp]
    public string $dateRange;

    public function mount(array $initialData = []): void
    {
        $this->userId = $initialData['user_id'] ?? '';
        $this->dateRange = $initialData['date_range'] ?? 'today';

        $this->loadStats();
    }
}

How do I access the current user in a component?

use App\Framework\Auth\CurrentUser;

final class UserDashboard extends LiveComponent
{
    public function __construct(
        private readonly CurrentUser $currentUser
    ) {}

    public function mount(): void
    {
        $user = $this->currentUser->get();

        $this->userId = $user->id;
        $this->userName = $user->name;
    }
}

Can I nest LiveComponents?

Yes, but with caveats:

// Parent component
final class Dashboard extends LiveComponent
{
    public function render(): string
    {
        return $this->view('dashboard', [
            'statsWidget' => LiveComponent::mount(StatsWidget::class),
            'activityFeed' => LiveComponent::mount(ActivityFeed::class)
        ]);
    }
}

Template:

<div data-component-id="{component_id}">
    <h1>Dashboard</h1>

    <!-- Nested component 1 -->
    {statsWidget}

    <!-- Nested component 2 -->
    {activityFeed}
</div>

Limitations:

  • Each component has its own lifecycle
  • Parent changes don't automatically update children
  • Use component communication for coordination

State Management

What types can I use for LiveProp?

// ✅ Supported types
#[LiveProp] public string $name;
#[LiveProp] public int $count;
#[LiveProp] public float $price;
#[LiveProp] public bool $active;
#[LiveProp] public array $items;

// ✅ Value Objects (with proper serialization)
#[LiveProp] public Email $email;
#[LiveProp] public Money $total;

// ❌ Not supported
#[LiveProp] public \DateTime $date;  // Use DateTimeImmutable or Timestamp VO
#[LiveProp] public UserRepository $repo;  // Use DI, not LiveProp
#[LiveProp] public callable $callback;  // Closures can't be serialized

How do I handle large datasets?

Use pagination:

final class ProductList extends LiveComponent
{
    #[LiveProp]
    public int $page = 1;

    #[LiveProp]
    public int $perPage = 50;

    private array $products = [];

    public function mount(): void
    {
        $this->loadPage();
    }

    #[LiveAction]
    #[Fragment('product-list')]
    public function loadPage(int $page = null): void
    {
        $this->page = $page ?? $this->page;

        // Only load current page
        $this->products = $this->productRepository->paginate(
            page: $this->page,
            perPage: $this->perPage
        );
    }
}

Don't serialize large datasets:

// ❌ Bad - serializes entire dataset
#[LiveProp]
public array $allProducts = [];  // 10,000 products

// ✅ Good - only serialize IDs
#[LiveProp]
public array $productIds = [];

private array $products = [];  // Not serialized

public function hydrate(array $state): void
{
    parent::hydrate($state);

    // Reload products from IDs
    $this->products = $this->productRepository->findByIds($this->productIds);
}

Can I use sessions/cookies in components?

Yes:

use App\Framework\Http\Session;

final class PreferencesComponent extends LiveComponent
{
    public function __construct(
        private readonly Session $session
    ) {}

    #[LiveAction]
    public function savePreference(string $key, string $value): void
    {
        $this->session->set("preferences.{$key}", $value);
    }
}

Performance

How can I optimize component performance?

1. Use Fragment Rendering:

#[LiveAction]
#[Fragment('search-results')]
public function search(): void
{
    // Only re-render search results fragment
}

2. Enable Request Batching:

LiveComponent.configure({
    batchSize: 10,
    batchDebounce: 50
});

3. Cache Expensive Operations:

use App\Framework\Cache\Attributes\Cached;

#[Cached(ttl: Duration::fromMinutes(5))]
public function getStats(): array
{
    // Cached for 5 minutes
    return $this->analyticsService->getStats();
}

4. Use Optimistic UI:

#[LiveAction]
#[Optimistic(property: 'count', operation: 'increment')]
public function increment(): void
{
    $this->count++;
}

See Performance Guide for more strategies.


What's the maximum number of components per page?

Recommended: <50 components per page for optimal performance

Performance characteristics:

  • Each component: ~2MB memory (browser)
  • 50 components: ~100MB total
  • Network: Fragment updates minimize bandwidth

For high component counts:

  • Use lazy loading
  • Implement virtual scrolling
  • Consider pagination

How do I debug performance issues?

Enable DevTools:

LIVECOMPONENT_DEVTOOLS_ENABLED=true

Or via localStorage:

localStorage.setItem('livecomponent_devtools', 'true');
location.reload();

Monitor metrics:

  • Performance tab: Flamegraphs, timeline, memory profiling
  • Network tab: Request batching efficiency
  • Action log: Execution times

Security

Is CSRF protection enabled by default?

Yes, CSRF protection is automatic for all LiveComponent requests.

How it works:

  1. CSRF token in meta tag: <meta name="csrf-token" content="...">
  2. Automatically included in all requests
  3. Server validates token via CsrfMiddleware

Disable for specific routes (not recommended):

#[Route(path: '/public-api', method: Method::POST)]
#[SkipCsrf]
public function publicEndpoint(): JsonResult
{
    // Public endpoint without CSRF
}

How do I implement authorization?

use App\Framework\LiveComponents\Attributes\Authorize;

#[LiveAction]
#[Authorize(roles: ['admin'])]
public function deleteUser(string $userId): void
{
    $this->userService->delete($userId);
}

#[LiveAction]
#[Authorize(permissions: ['posts.edit'])]
public function updatePost(): void
{
    // Only users with posts.edit permission
}

Custom authorization:

#[LiveAction]
public function criticalAction(): void
{
    if (!$this->canPerformAction()) {
        throw new UnauthorizedException('Insufficient permissions');
    }

    // Perform action
}

private function canPerformAction(): bool
{
    return $this->currentUser->hasRole('admin')
        || $this->resource->ownedBy($this->currentUser->id);
}

How do I prevent SQL injection?

Framework uses parameterized queries automatically:

// ✅ Safe - framework handles parameterization
$users = $this->repository->findByEmail($this->email);

// ✅ Safe - query builder with bindings
$results = $this->db->query(
    'SELECT * FROM users WHERE email = ?',
    [$this->email]
);

// ❌ NEVER do this
$results = $this->db->query(
    "SELECT * FROM users WHERE email = '{$this->email}'"
);

Additional protection: WAF SQL injection layer blocks suspicious patterns.


Deployment

What do I need for production deployment?

Required:

  • PHP 8.4+ production environment
  • HTTPS enabled (required for CSRF/SSE)
  • CSRF protection enabled (LIVECOMPONENT_CSRF_PROTECTION=true)
  • Rate limiting configured
  • Compiled JavaScript assets (npm run build)

Recommended:

  • Redis for SSE connection pool
  • OPcache enabled (PHP 8.5 JIT)
  • CDN for static assets
  • Monitoring (performance metrics, error tracking)

See Security Guide - Production Checklist.


Can I use LiveComponents with a CDN?

Yes:

<!-- Compiled assets served from CDN -->
<script type="module" src="https://cdn.example.com/assets/js/livecomponent.min.js"></script>

Configure in build:

// vite.config.js
export default defineConfig({
    base: 'https://cdn.example.com/assets/'
});

How do I handle server errors in production?

Server-side:

use App\Framework\ErrorHandling\ErrorHandler;

try {
    $result = $this->executeAction();
} catch (\Exception $e) {
    $this->errorHandler->log($e);

    // Return user-friendly error
    return new ActionResult(
        success: false,
        message: 'An error occurred. Please try again.',
        errors: $this->environment->isProduction()
            ? []
            : [$e->getMessage()]
    );
}

Client-side:

window.addEventListener('livecomponent:error', (e) => {
    const { message, code } = e.detail;

    if (code === 'CSRF_TOKEN_EXPIRED') {
        LiveComponent.refreshCsrfToken();
    } else {
        showNotification(message, 'error');
    }
});

Testing

How do I test LiveComponents?

Unit Tests (Pest):

it('increments counter', function () {
    $component = new Counter();
    $component->mount();

    expect($component->count)->toBe(0);

    $component->increment();

    expect($component->count)->toBe(1);
});

Integration Tests:

it('renders component with correct data', function () {
    $component = new UserProfile();
    $component->mount(['user_id' => '123']);

    $html = $component->render();

    expect($html)->toContain('data-component-id');
    expect($html)->toContain('User Profile');
});

E2E Tests (Playwright):

test('user can submit form', async ({ page }) => {
    await page.goto('https://localhost/form');

    await page.fill('[data-lc-model="name"]', 'John Doe');
    await page.click('[data-lc-action="submit"]');

    await expect(page.locator('.success-message')).toBeVisible();
});

Troubleshooting

Component not updating after action

Check:

  1. Is #[LiveProp] attribute present?
  2. Is property being modified in action?
  3. Check browser console for JavaScript errors
  4. Verify CSRF token is valid

Debug:

// Get component state
const component = LiveComponent.getComponent('component-id');
console.log('Current state:', component.state);

// Monitor action execution
window.addEventListener('livecomponent:action-executed', (e) => {
    console.log('Action result:', e.detail);
});

"CSRF token mismatch" error

Causes:

  • Token expired (default: 2 hours)
  • Session cleared
  • Token not in meta tag

Solutions:

<!-- Ensure CSRF meta tag exists -->
<meta name="csrf-token" content="{csrf_token}">
// Refresh token
LiveComponent.refreshCsrfToken().then(() => {
    LiveComponent.retryLastAction();
});

SSE connection not working

Check:

  1. HTTPS enabled? (Required for SSE)
  2. Port 443 accessible?
  3. Firewall blocking SSE endpoint?

Debug:

// Monitor SSE events
window.addEventListener('livecomponent:sse-connected', () => {
    console.log('SSE connected');
});

window.addEventListener('livecomponent:sse-error', (e) => {
    console.error('SSE error:', e.detail);
});

High memory usage

Causes:

  • Too many components (>100)
  • Large serialized state
  • Memory leaks in actions

Solutions:

  1. Reduce component count: Use pagination/lazy loading
  2. Minimize state: Don't serialize large datasets
  3. Clean up in unmount:
public function unmount(): void
{
    $this->largeDataset = [];
    gc_collect_cycles();
}

Integration

Can I use LiveComponents with Alpine.js?

Yes:

<div
    data-component-id="{component_id}"
    x-data="{ expanded: false }"
>
    <!-- LiveComponent state -->
    <p>Count: {count}</p>
    <button data-lc-action="increment">Increment</button>

    <!-- Alpine.js state -->
    <button @click="expanded = !expanded">Toggle</button>
    <div x-show="expanded">
        Expandable content
    </div>
</div>

Best practices:

  • Use LiveComponents for server state
  • Use Alpine.js for client-only UI state

Can I use LiveComponents with htmx?

They can coexist, but don't mix on the same element:

<!-- ✅ Separate usage -->
<div data-component-id="lc-component">
    LiveComponent content
</div>

<div hx-get="/endpoint">
    htmx content
</div>

<!-- ❌ Don't mix on same element -->
<div data-component-id="..." hx-post="...">
    Conflict!
</div>

Advanced

Can I create custom directives?

Yes, extend the template processor:

use App\Framework\Template\TemplateProcessor;

final readonly class CustomDirectiveProcessor implements TemplateProcessor
{
    public function process(string $template, array $data): string
    {
        // Process custom directive: <custom-tag>
        return preg_replace_callback(
            '/<custom-tag>(.+?)<\/custom-tag>/',
            fn($matches) => $this->processCustomTag($matches[1]),
            $template
        );
    }
}

Register via attribute discovery:

#[TemplateProcessor(priority: 100)]
final readonly class CustomDirectiveProcessor { ... }

How do I implement server-side rendering (SSR)?

LiveComponents is server-side rendering - components render on the server and stream HTML to the client.

For initial page load SEO:

// Controller returns fully rendered HTML
public function page(): ViewResult
{
    return new ViewResult('page', [
        'component' => LiveComponent::mount(InteractiveComponent::class)
    ]);
}

HTML is immediately crawlable by search engines.


Can I use WebSockets instead of SSE?

Framework uses SSE by design because:

  • Simpler than WebSockets for server-to-client
  • Automatic reconnection
  • HTTP/2 multiplexing
  • Lower overhead for one-way updates

For two-way real-time: Consider GraphQL subscriptions or framework's WebSocket support (separate from LiveComponents).


Getting Help

Where can I find more examples?

  • GitHub: Example components in src/Application/LiveComponents/
  • Tests: E2E tests in tests/e2e/livecomponents-*.spec.js
  • Documentation: Getting Started, Advanced Features

How do I report bugs?

Security issues: security@example.com

Bug reports: GitHub Issues with:

  • Component code
  • Expected vs actual behavior
  • Browser console errors
  • Steps to reproduce

How do I request features?

Open GitHub Discussion with:

  • Use case description
  • Proposed API/syntax
  • Alternative solutions considered

Next: Troubleshooting Guide