# LiveComponents Performance Guide **Production-Ready Performance Optimization and Monitoring** This guide covers performance optimization strategies, profiling techniques, and best practices for building high-performance LiveComponents applications. ## Performance Targets ### Response Time Goals - **Simple Actions**: <100ms server processing - **Complex Actions**: <250ms server processing - **Initial Page Load**: <2s Time to Interactive - **Subsequent Interactions**: <50ms perceived latency (optimistic UI) ### Network Efficiency - **Request Batching**: 80%+ reduction in HTTP requests - **Payload Size**: <5KB per action response (average) - **Fragment Updates**: 70%+ reduction in DOM updates ### Resource Usage - **Memory**: <2MB per component instance - **CPU**: <5% for idle components - **Network**: <100KB/minute for active SSE connections --- ## 1. Fragment-Based Rendering ### Overview Fragment rendering updates only changed parts of the DOM instead of re-rendering entire components. **Performance Impact**: - 70-90% reduction in DOM updates - 50-80% faster perceived performance - Reduced browser reflow/repaint cycles ### Implementation ```html

Dashboard

Active Users: {activeUsers}

Sessions: {sessions}

{activity.message}
``` ### Server-Side Fragment Rendering ```php use App\Framework\LiveComponents\Attributes\Fragment; final class Dashboard extends LiveComponent { #[LiveProp] public int $activeUsers = 0; #[LiveProp] public int $sessions = 0; #[LiveAction] #[Fragment('user-stats')] // Only re-render this fragment public function updateUserStats(): void { $this->activeUsers = $this->statsService->getActiveUsers(); $this->sessions = $this->statsService->getSessions(); // Activity feed and settings remain unchanged } #[LiveAction] #[Fragment(['user-stats', 'activity-feed'])] // Update multiple fragments public function refreshDashboard(): void { $this->updateUserStats(); $this->loadRecentActivity(); } } ``` ### Fragment Performance Metrics ```javascript // Monitor fragment update performance window.addEventListener('livecomponent:fragment-updated', (e) => { console.log('Fragment update performance:', { fragment: e.detail.fragmentName, updateTime: e.detail.duration, nodesChanged: e.detail.nodesChanged, payloadSize: e.detail.payloadSize }); }); ``` ### Best Practices **1. Granular Fragments**: Create fragments for independently changing sections ```html
{todo.title}
{todo.title}
``` **2. Static Content Outside Fragments**: Keep unchanged content outside fragments ```html

Product Catalog

``` **3. Nested Fragments**: Use nested fragments for hierarchical updates ```html
``` --- ## 2. Request Batching ### Overview Automatic batching combines multiple rapid actions into a single HTTP request. **Performance Impact**: - 80-95% reduction in HTTP requests - Lower server load - Reduced network overhead - Better mobile performance ### Configuration ```env # .env configuration LIVECOMPONENT_BATCH_SIZE=10 # Max actions per batch LIVECOMPONENT_BATCH_DEBOUNCE=50 # Debounce delay in ms ``` ### How It Works ``` User Action 1 → Queue User Action 2 → Queue } 50ms debounce User Action 3 → Queue ↓ Batch Request → Server ↓ Single HTTP POST with 3 actions ``` ### Monitoring Batch Efficiency ```javascript // Track batching performance window.addEventListener('livecomponent:batch-sent', (e) => { const efficiency = (e.detail.actionsCount - 1) / e.detail.actionsCount; console.log('Batch efficiency:', { actions: e.detail.actionsCount, requestsSaved: e.detail.actionsCount - 1, efficiency: (efficiency * 100).toFixed(1) + '%', payloadSize: e.detail.payloadSize }); }); ``` ### Batch-Aware Action Design ```php // Design actions to be batch-friendly final class ProductList extends LiveComponent { #[LiveAction] public function addToCart(string $productId): void { // Fast, stateless operation - perfect for batching $this->cart->add($productId); } #[LiveAction] public function updateQuantity(string $productId, int $quantity): void { // Independent operation - batches well $this->cart->updateQuantity($productId, $quantity); } #[LiveAction] public function applyDiscount(string $code): void { // Depends on cart state - still batches, processed in order $this->cart->applyDiscount($code); } } ``` ### Best Practices **1. Design for Batching**: Keep actions small and independent **2. Avoid Side Effects**: Actions should be idempotent where possible **3. Order Matters**: Batch processes actions in order received **4. Monitor Efficiency**: Track batch savings in production --- ## 3. Optimistic UI Updates ### Overview Optimistic UI updates the interface immediately on user action, then reconciles with server response. **Performance Impact**: - Perceived latency: <50ms (vs 100-300ms without) - Improved user experience - Higher conversion rates - Better mobile UX ### Implementation ```html ``` ### Server-Side Configuration ```php use App\Framework\LiveComponents\Attributes\Optimistic; final class PostInteraction extends LiveComponent { #[LiveProp] public bool $isLiked = false; #[LiveProp] public int $likeCount = 0; #[LiveAction] #[Optimistic(property: 'isLiked', operation: 'toggle')] #[Optimistic(property: 'likeCount', operation: 'increment')] public function toggleLike(): void { $this->isLiked = !$this->isLiked; $this->likeCount += $this->isLiked ? 1 : -1; // Persist to database $this->postService->toggleLike($this->postId); } } ``` ### Error Handling & Rollback ```javascript // Automatic rollback on server error window.addEventListener('livecomponent:optimistic-rollback', (e) => { console.warn('Optimistic update rolled back:', { action: e.detail.action, reason: e.detail.error, originalState: e.detail.stateBeforeOptimistic }); // Show user-friendly error showNotification('Action failed, changes reverted', 'error'); }); ``` ### Performance Monitoring ```javascript // Track optimistic update performance let optimisticStart = null; window.addEventListener('livecomponent:action-start', (e) => { if (e.detail.optimistic) { optimisticStart = performance.now(); } }); window.addEventListener('livecomponent:optimistic-applied', (e) => { const duration = performance.now() - optimisticStart; console.log('Optimistic UI performance:', { action: e.detail.action, uiUpdateTime: duration, target: '< 50ms', pass: duration < 50 }); }); ``` ### Best Practices **1. Use for Fast Actions**: Like buttons, counters, toggles **2. Avoid for Critical Actions**: Don't use for payments, deletions **3. Provide Feedback**: Show loading state during server confirmation **4. Handle Conflicts**: Implement conflict resolution for concurrent updates ```php // Conflict resolution example #[LiveAction] #[Optimistic(conflictResolution: 'server-wins')] public function updateProfile(string $bio): void { // Check for conflicts if ($this->hasConflict($bio)) { throw new OptimisticConflictException('Profile updated by another session'); } $this->bio = $bio; $this->profileService->update($this->userId, $bio); } ``` --- ## 4. Caching Strategies ### Component-Level Caching ```php use App\Framework\Cache\Attributes\Cached; use App\Framework\Core\ValueObjects\Duration; final class ProductCatalog extends LiveComponent { #[Cached(ttl: Duration::fromMinutes(5), key: 'products.featured')] public function getFeaturedProducts(): array { // Expensive database query cached for 5 minutes return $this->productRepository->getFeatured(limit: 10); } #[LiveAction] public function loadMore(): void { // Use cached featured products $this->products = $this->getFeaturedProducts(); } } ``` ### Fragment Caching ```php final class Dashboard extends LiveComponent { #[LiveAction] #[Fragment('user-stats')] #[Cached(ttl: Duration::fromSeconds(30), key: 'dashboard.stats.{userId}')] public function refreshStats(): void { // Expensive stats calculation cached per user $this->stats = $this->analyticsService->getUserStats($this->userId); } } ``` ### Client-Side Caching ```javascript // Enable client-side component state caching LiveComponent.configure({ cacheComponentState: true, cacheStrategy: 'sessionStorage', // or 'localStorage' cacheTTL: 300000 // 5 minutes }); ``` ### Cache Invalidation ```php use App\Framework\Cache\CacheInvalidator; final class ProductService { public function updateProduct(Product $product): void { $this->repository->save($product); // Invalidate related caches $this->cacheInvalidator->invalidate([ "products.featured", "products.{$product->category}", "product.{$product->id}" ]); } } ``` --- ## 5. Server-Sent Events (SSE) Optimization ### Connection Pooling ```php use App\Framework\LiveComponents\SSE\SseConnectionPool; final class RealtimeDashboard extends LiveComponent { public function mount(): void { // Reuse existing SSE connection $this->ssePool->subscribe( componentId: $this->id, channels: ['user.updates', 'system.notifications'] ); } public function unmount(): void { // Release connection when component unmounts $this->ssePool->unsubscribe($this->id); } } ``` ### Message Batching ```php // Server-side: Batch SSE messages final class SseMessageBatcher { private array $pendingMessages = []; public function queue(string $channel, array $data): void { $this->pendingMessages[$channel][] = $data; } public function flush(): void { foreach ($this->pendingMessages as $channel => $messages) { $this->sseService->broadcast($channel, [ 'batch' => true, 'messages' => $messages ]); } $this->pendingMessages = []; } } // Flush every 100ms $this->scheduler->schedule( 'sse-batch-flush', IntervalSchedule::every(Duration::fromMilliseconds(100)), fn() => $this->batcher->flush() ); ``` ### Client-Side Optimization ```javascript // Debounce rapid SSE updates const debouncedUpdate = debounce((data) => { LiveComponent.processUpdate(data); }, 50); const eventSource = new EventSource('/sse/updates'); eventSource.addEventListener('component-update', (e) => { debouncedUpdate(JSON.parse(e.data)); }); ``` --- ## 6. Memory Management ### Component Lifecycle ```php final class DataGrid extends LiveComponent { private array $largeDataset = []; #[LiveAction] public function loadData(): void { // Load data $this->largeDataset = $this->dataService->getLargeDataset(); } public function unmount(): void { // Clean up memory when component unmounts $this->largeDataset = []; gc_collect_cycles(); } } ``` ### Pagination for Large Datasets ```php final class UserList extends LiveComponent { #[LiveProp] public int $page = 1; #[LiveProp] public int $perPage = 50; #[LiveAction] #[Fragment('user-list')] public function loadPage(int $page): void { $this->page = $page; // Only load current page, not entire dataset $this->users = $this->userRepository->paginate( page: $this->page, perPage: $this->perPage ); } } ``` ### Virtual Scrolling ```html
{item.name}
``` ```php final class VirtualList extends LiveComponent { #[LiveProp] public int $scrollOffset = 0; #[LiveAction] #[Fragment('visible-items')] public function updateViewport(int $offset): void { $this->scrollOffset = $offset; // Calculate visible items based on scroll position $startIndex = floor($offset / 50); $this->visibleItems = array_slice( $this->allItems, $startIndex, 20 // Visible items ); } } ``` --- ## 7. Network Optimization ### Payload Compression ```php // Enable gzip compression for JSON responses final class LiveComponentResponse { public function toJson(): string { $json = json_encode($this->data); // Compress if payload > 1KB if (strlen($json) > 1024) { return gzencode($json, 6); } return $json; } } ``` ### Payload Minimization ```php final class ProductList extends LiveComponent { #[LiveAction] #[Fragment('products')] public function loadProducts(): void { // ❌ Don't send full objects // $this->products = $this->repository->findAll(); // ✅ Send only necessary data $this->products = $this->repository->findAll()->map(fn($p) => [ 'id' => $p->id, 'name' => $p->name, 'price' => $p->price->toDecimal() ]); } } ``` ### Delta Updates ```php use App\Framework\LiveComponents\Attributes\DeltaUpdate; final class StockTicker extends LiveComponent { #[LiveProp] public array $stocks = []; #[LiveAction] #[DeltaUpdate] // Only send changed stock prices public function updatePrices(): void { $updatedStocks = $this->stockService->getUpdatedPrices(); foreach ($updatedStocks as $symbol => $price) { $this->stocks[$symbol]['price'] = $price; } // Framework only sends changed prices, not entire array } } ``` --- ## 8. Performance Profiling ### DevTools Performance Panel ```javascript // Enable performance profiling localStorage.setItem('livecomponent_devtools', 'true'); localStorage.setItem('livecomponent_profiling', 'true'); // Access DevTools → Performance Tab // - Flamegraph visualization // - Timeline view // - Memory profiling ``` ### Custom Performance Marks ```php use App\Framework\Performance\PerformanceTracker; final class ComplexComponent extends LiveComponent { #[LiveAction] public function complexOperation(): void { $this->performance->mark('operation.start'); $this->stepOne(); $this->performance->mark('step1.complete'); $this->stepTwo(); $this->performance->mark('step2.complete'); $this->stepThree(); $this->performance->mark('operation.end'); // Measures automatically logged } } ``` ### Server-Side Profiling ```php // Track component render time final class PerformanceMiddleware { public function process(Request $request, callable $next): Response { $start = hrtime(true); $response = $next($request); $duration = (hrtime(true) - $start) / 1e6; // Convert to ms $response->headers->set('X-Render-Time', (string)$duration); if ($duration > 100) { $this->logger->warning('Slow component render', [ 'component' => $request->attributes->get('component'), 'action' => $request->attributes->get('action'), 'duration_ms' => $duration ]); } return $response; } } ``` --- ## 9. Database Query Optimization ### N+1 Query Prevention ```php // ❌ N+1 query problem final class OrderList extends LiveComponent { public function render(): string { foreach ($this->orders as $order) { $customer = $this->customerRepository->find($order->customerId); // N+1: One query per order } } } // ✅ Eager loading final class OrderList extends LiveComponent { public function mount(): void { // Single query with JOIN $this->orders = $this->orderRepository->findWithCustomers($this->filters); } } ``` ### Query Result Caching ```php use App\Framework\Database\Attributes\CachedQuery; final readonly class ProductRepository { #[CachedQuery(ttl: Duration::fromMinutes(10), key: 'products.category.{category}')] public function findByCategory(string $category): array { return $this->db->query( 'SELECT * FROM products WHERE category = ?', [$category] ); } } ``` ### Connection Pooling ```env # Database connection pool configuration DB_POOL_MIN_CONNECTIONS=5 DB_POOL_MAX_CONNECTIONS=20 DB_POOL_WAIT_TIMEOUT=5000 ``` --- ## 10. Production Monitoring ### Performance Metrics Collection ```php use App\Framework\Metrics\MetricsCollector; final class LiveComponentMetrics { public function recordActionPerformance( string $component, string $action, float $duration ): void { $this->metrics->timing('livecomponent.action.duration', $duration, [ 'component' => $component, 'action' => $action ]); if ($duration > 100) { $this->metrics->increment('livecomponent.action.slow', [ 'component' => $component ]); } } } ``` ### Real-User Monitoring (RUM) ```javascript // Client-side performance tracking const performanceObserver = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.name.includes('livecomponent')) { // Send to analytics analytics.track('LiveComponent Performance', { action: entry.name, duration: entry.duration, startTime: entry.startTime }); } } }); performanceObserver.observe({ entryTypes: ['measure'] }); ``` ### Alerting Thresholds ```php // Alert on performance degradation final class PerformanceAlerting { private const THRESHOLDS = [ 'action.duration.p95' => 200, // 95th percentile <200ms 'action.duration.p99' => 500, // 99th percentile <500ms 'fragment.update.duration' => 50, 'batch.efficiency' => 0.8 // 80%+ batching ]; public function checkThresholds(): void { $metrics = $this->metrics->getAggregated(Duration::fromMinutes(5)); foreach (self::THRESHOLDS as $metric => $threshold) { if ($metrics[$metric] > $threshold) { $this->alerting->send( level: AlertLevel::WARNING, message: "Performance threshold exceeded: {$metric}", data: ['value' => $metrics[$metric], 'threshold' => $threshold] ); } } } } ``` --- ## Performance Benchmarks ### Framework Performance (PHP 8.5) Based on `php85-framework-integration.md`: - **Average Performance Improvement**: +17% - **OPcache with JIT**: +35% for compute-intensive operations - **Sodium Crypto**: +22% encryption/decryption - **Random Extension**: +18% token generation ### LiveComponents Benchmarks **Action Latency** (1000 requests): - Simple toggle: 45ms (p95), 78ms (p99) - Database query: 89ms (p95), 142ms (p99) - Complex calculation: 156ms (p95), 234ms (p99) **Request Batching Efficiency**: - 3 actions batched: 67% reduction (3 requests → 1) - 5 actions batched: 80% reduction (5 requests → 1) - 10 actions batched: 90% reduction (10 requests → 1) **Fragment Updates**: - Full component: 28ms DOM update - Single fragment: 8ms DOM update - 3 fragments: 15ms DOM update - Efficiency: 71% faster **Optimistic UI**: - UI update: <50ms (100% of samples) - Server confirmation: 95ms (average) - Perceived latency reduction: 65% **Memory Usage**: - Component instance: 1.2MB average - 10 components: 8.5MB total - SSE connection: 512KB per connection --- ## Optimization Checklist ### Before Production - [ ] Enable fragment rendering for large components - [ ] Configure request batching (10 actions, 50ms debounce) - [ ] Implement optimistic UI for interactive elements - [ ] Add caching for expensive operations (5-10 min TTL) - [ ] Optimize database queries (prevent N+1) - [ ] Enable payload compression (>1KB responses) - [ ] Configure connection pooling (DB + SSE) - [ ] Set up performance monitoring (metrics + alerts) - [ ] Test with DevTools performance profiler - [ ] Validate performance budgets (<100ms p95) ### Monitoring Dashboards - [ ] Action latency (p50, p95, p99) - [ ] Request batching efficiency - [ ] Fragment update performance - [ ] Memory usage per component - [ ] SSE connection count - [ ] Cache hit rates - [ ] Database query times - [ ] Error rates ### Performance Budgets ```yaml performance_budgets: action_latency: p95: 100ms p99: 250ms fragment_update: average: 20ms p95: 50ms batching_efficiency: minimum: 70% memory_per_component: maximum: 3MB sse_connections: maximum: 100 per server cache_hit_rate: minimum: 85% ``` --- ## Best Practices Summary 1. **Fragment Everything**: Break components into granular fragments 2. **Batch by Default**: Design actions to be batch-friendly 3. **Optimistic When Safe**: Use optimistic UI for non-critical actions 4. **Cache Aggressively**: Cache expensive operations (5-10 min TTL) 5. **Monitor Always**: Track performance metrics in production 6. **Profile Regularly**: Use DevTools for performance analysis 7. **Optimize Queries**: Prevent N+1, use eager loading 8. **Compress Payloads**: Enable gzip for responses >1KB 9. **Paginate Large Data**: Never send entire datasets 10. **Budget Performance**: Set and enforce performance budgets --- **Next**: [API Reference](api-reference.md) →