- 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.
20 KiB
LiveComponents Troubleshooting Guide
Common Issues and Solutions
This guide helps diagnose and resolve common LiveComponents problems.
Table of Contents
- Component Not Responding
- State Synchronization Issues
- CSRF Token Errors
- Rate Limiting Issues
- SSE Connection Problems
- Performance Issues
- File Upload Problems
- Fragment Rendering Issues
- Browser Compatibility
- Debugging Tools
Component Not Responding
Symptoms
- Clicking buttons does nothing
- Forms don't submit
- No network requests in DevTools
- Component appears static
Diagnostic Steps
1. Check Component Initialization
// Open browser console
console.log(LiveComponent.components);
// Should show active components
// If empty, component not initialized
2. Verify Component Markup
<!-- Required attributes -->
<div data-component-id="{component_id}" data-component-name="ComponentName">
<!-- Content -->
</div>
3. Check JavaScript Loading
// In browser console
typeof LiveComponent !== 'undefined' // Should be true
4. Inspect Browser Console
F12 → Console Tab
Look for JavaScript errors
Common Causes & Solutions
Missing data-component-id
<!-- ❌ Wrong - missing required attribute -->
<div class="component">
<button data-lc-action="save">Save</button>
</div>
<!-- ✅ Correct -->
<div data-component-id="{component_id}" data-component-name="MyComponent">
<button data-lc-action="save">Save</button>
</div>
JavaScript Not Loaded
# Rebuild JavaScript assets
npm run build
# Check if file exists
ls public/assets/js/livecomponent.js
# Development mode
npm run dev
HTTPS Not Enabled
# LiveComponents requires HTTPS
# Start with HTTPS
make up
# Access via
https://localhost # ✅ Correct
http://localhost # ❌ Wrong - will fail
Component Class Not Found
// Verify class exists and namespace is correct
namespace App\Application\LiveComponents;
final class MyComponent extends LiveComponent
{
// Implementation
}
// In controller
LiveComponent::mount(MyComponent::class) // ✅
LiveComponent::mount('MyComponent') // ❌ Wrong
State Synchronization Issues
Symptoms
- Property changes don't reflect in UI
- UI updates but server state wrong
- State resets unexpectedly
Diagnostic Steps
1. Check LiveProp Attribute
// ❌ Wrong - missing attribute
public string $name = '';
// ✅ Correct
#[LiveProp]
public string $name = '';
2. Verify Property Type Compatibility
// ✅ Supported types
#[LiveProp] public string $text;
#[LiveProp] public int $count;
#[LiveProp] public float $price;
#[LiveProp] public bool $active;
#[LiveProp] public array $items;
// ❌ Not supported - can't serialize
#[LiveProp] public \Closure $callback;
#[LiveProp] public Resource $handle;
#[LiveProp] public PDO $connection;
3. Inspect Current State
// Get component state
const component = LiveComponent.getComponent('component-id');
console.log('Current state:', component.state);
Common Causes & Solutions
Property Not Marked as LiveProp
// Problem: Property changes not synced
public string $searchTerm = ''; // ❌ Missing #[LiveProp]
#[LiveAction]
public function search(): void
{
// $this->searchTerm not synced with client
$this->results = $this->searchService->search($this->searchTerm);
}
// Solution: Add attribute
#[LiveProp]
public string $searchTerm = ''; // ✅ Now syncs
Complex Object Serialization
// Problem: Object can't be serialized
#[LiveProp]
public User $user; // ❌ Complex object
// Solution: Use Value Object or primitives
#[LiveProp]
public string $userId;
private User $user; // Not serialized
public function hydrate(array $state): void
{
parent::hydrate($state);
// Reload user from ID
$this->user = $this->userRepository->find($this->userId);
}
State Checksum Mismatch
// Monitor checksum errors
window.addEventListener('livecomponent:error', (e) => {
if (e.detail.code === 'CHECKSUM_MISMATCH') {
console.error('State corrupted - refreshing component');
LiveComponent.refresh(e.detail.componentId);
}
});
CSRF Token Errors
Symptoms
- "CSRF token mismatch" error
- 403 Forbidden responses
- Actions fail silently
Diagnostic Steps
1. Check CSRF Meta Tag
<!-- Must be in <head> -->
<meta name="csrf-token" content="{csrf_token}">
2. Verify Token in Requests
F12 → Network Tab → Select request → Headers
Should have: X-CSRF-Token: {token}
3. Check Token Expiry
// .env configuration
CSRF_TOKEN_LIFETIME=7200 // 2 hours (default)
Common Causes & Solutions
Missing CSRF Meta Tag
<!-- ❌ Wrong - no CSRF token -->
<head>
<title>My App</title>
</head>
<!-- ✅ Correct - token included -->
<head>
<title>My App</title>
<meta name="csrf-token" content="{csrf_token}">
</head>
Token Expired
// Automatic token refresh
window.addEventListener('livecomponent:error', (e) => {
if (e.detail.code === 'CSRF_TOKEN_EXPIRED') {
// Refresh token
LiveComponent.refreshCsrfToken().then(() => {
// Retry failed action
LiveComponent.retryLastAction();
});
}
});
Session Cleared
// Problem: Session expired/cleared
// Solution: Redirect to login
window.addEventListener('livecomponent:error', (e) => {
if (e.detail.code === 'SESSION_EXPIRED') {
window.location.href = '/login?redirect=' + window.location.pathname;
}
});
Development vs Production
# Development - lenient
CSRF_TOKEN_LIFETIME=86400 # 24 hours
# Production - strict
CSRF_TOKEN_LIFETIME=3600 # 1 hour
CSRF_REGENERATE_ON_ACTION=true
Rate Limiting Issues
Symptoms
- "Too many requests" error (429)
- Actions blocked after rapid clicks
- "Retry-After" headers in response
Diagnostic Steps
1. Check Rate Limit Configuration
LIVECOMPONENT_RATE_LIMIT=60 # Default: 60/minute
LIVECOMPONENT_RATE_LIMIT_WINDOW=60 # Window in seconds
2. Monitor Rate Limit Events
window.addEventListener('livecomponent:rate-limited', (e) => {
console.warn('Rate limited:', {
action: e.detail.action,
retryAfter: e.detail.retryAfter
});
});
3. Check Component-Specific Limits
#[RateLimit(requests: 10, window: 60)]
final class SearchComponent extends LiveComponent
{
// More restrictive than global limit
}
Common Causes & Solutions
Too Aggressive Rate Limit
# Problem: Rate limit too low
LIVECOMPONENT_RATE_LIMIT=10 # Too restrictive
# Solution: Increase for normal usage
LIVECOMPONENT_RATE_LIMIT=60 # More reasonable
Rapid User Actions
// Problem: User clicking too fast
#[LiveAction]
public function search(): void
{
// Triggered on every keystroke
}
// Solution: Use debouncing
<input
type="text"
data-lc-model.debounce.500="searchTerm"
value="{searchTerm}"
/>
Bot/Scraper Activity
use App\Framework\Http\Middlewares\RateLimitMiddleware;
// Implement stricter limits for suspicious patterns
#[RateLimit(requests: 5, window: 300)] // 5 per 5 minutes
final class PublicApiComponent extends LiveComponent
{
// Public-facing, needs protection
}
SSE Connection Problems
Symptoms
- Real-time updates not working
- Connection drops frequently
- "SSE disconnected" in console
Diagnostic Steps
1. Verify HTTPS
SSE requires HTTPS in production
https://localhost ✅
http://localhost ❌
2. Check SSE Configuration
LIVECOMPONENT_SSE_ENABLED=true
LIVECOMPONENT_SSE_HEARTBEAT=15 # Heartbeat interval
3. Monitor SSE Connection
window.addEventListener('livecomponent:sse-connected', () => {
console.log('SSE connected');
});
window.addEventListener('livecomponent:sse-disconnected', (e) => {
console.error('SSE disconnected:', e.detail.reason);
});
4. Check Server Logs
docker logs php | grep SSE
Common Causes & Solutions
HTTPS Not Enabled
# Problem: HTTP used in production
# Solution: Enable HTTPS
# Development
make up # Automatically uses HTTPS
# Production
# Configure SSL certificates in nginx/apache
Firewall Blocking SSE
# nginx configuration
location /livecomponent/sse {
proxy_pass http://php-fpm;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 86400s; # 24 hours
}
Connection Timeout
// Increase SSE timeout
set_time_limit(0); // No timeout
ignore_user_abort(true);
Auto-Reconnection Not Working
// Manual reconnection logic
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;
window.addEventListener('livecomponent:sse-disconnected', (e) => {
if (reconnectAttempts < maxReconnectAttempts) {
const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 30000);
setTimeout(() => {
console.log(`Reconnecting SSE (attempt ${reconnectAttempts + 1})`);
LiveComponent.reconnectSse(e.detail.componentId);
reconnectAttempts++;
}, delay);
}
});
window.addEventListener('livecomponent:sse-connected', () => {
reconnectAttempts = 0; // Reset on successful connection
});
Performance Issues
Symptoms
- Slow action responses (>500ms)
- UI freezes during updates
- High memory usage
- Browser lag
Diagnostic Steps
1. Enable Performance Profiling
localStorage.setItem('livecomponent_devtools', 'true');
localStorage.setItem('livecomponent_profiling', 'true');
location.reload();
2. Check Action Latency
window.addEventListener('livecomponent:action-executed', (e) => {
if (e.detail.duration > 200) {
console.warn('Slow action:', {
action: e.detail.action,
duration: e.detail.duration
});
}
});
3. Monitor Memory Usage
F12 → Performance Tab → Memory
Record session and analyze heap snapshots
4. Analyze Network Requests
F12 → Network Tab
Check:
- Request count (should be low with batching)
- Payload sizes
- Response times
Common Causes & Solutions
Large Component State
// Problem: Serializing too much data
#[LiveProp]
public array $allProducts = []; // ❌ 10,000 products
// Solution: Pagination
#[LiveProp]
public int $page = 1;
#[LiveProp]
public int $perPage = 50;
private array $products = []; // ✅ Not serialized
public function mount(): void
{
$this->loadPage();
}
private function loadPage(): void
{
$this->products = $this->productRepository->paginate(
page: $this->page,
perPage: $this->perPage
);
}
No Fragment Rendering
// Problem: Re-rendering entire component
#[LiveAction]
public function updateStats(): void
{
$this->stats = $this->calculateStats();
// Full component re-render ❌
}
// Solution: Use fragments
#[LiveAction]
#[Fragment('stats-section')]
public function updateStats(): void
{
$this->stats = $this->calculateStats();
// Only stats fragment re-renders ✅
}
N+1 Query Problem
// Problem: N+1 queries
public function render(): string
{
foreach ($this->orders as $order) {
$customer = $this->customerRepository->find($order->customerId);
// ❌ Query per order
}
}
// Solution: Eager loading
public function mount(): void
{
$this->orders = $this->orderRepository->findWithCustomers();
// ✅ Single query with JOIN
}
Too Many Components
Problem: >100 components on page
Solution:
1. Lazy load components
2. Use virtual scrolling
3. Implement pagination
See Performance Guide for comprehensive optimization strategies.
File Upload Problems
Symptoms
- Upload fails silently
- Progress stuck at 0%
- Chunk upload errors
- File size exceeded errors
Diagnostic Steps
1. Check Upload Configuration
<input
type="file"
data-lc-upload="handleUpload"
data-chunk-size="1048576"
data-max-file-size="104857600"
/>
2. Monitor Upload Events
window.addEventListener('livecomponent:upload-progress', (e) => {
console.log(`Upload: ${e.detail.progress}%`);
});
window.addEventListener('livecomponent:upload-error', (e) => {
console.error('Upload failed:', e.detail.error);
});
3. Check Server Limits
// php.ini
upload_max_filesize = 100M
post_max_size = 100M
max_execution_time = 300
Common Causes & Solutions
File Size Exceeds Limit
<!-- Problem: No validation -->
<input type="file" data-lc-upload="handleUpload" />
<!-- Solution: Set max size -->
<input
type="file"
data-lc-upload="handleUpload"
data-max-file-size="104857600"
accept=".pdf,.jpg,.png"
/>
// Client-side validation
window.addEventListener('livecomponent:upload-error', (e) => {
if (e.detail.code === 'FILE_TOO_LARGE') {
alert(`File too large. Max size: ${e.detail.maxSize / 1024 / 1024}MB`);
}
});
Chunk Upload Failure
// Server-side: Implement retry logic
final class ChunkUploadHandler
{
public function storeChunk(
string $uploadId,
int $chunkIndex,
string $data
): void {
$attempts = 0;
$maxAttempts = 3;
while ($attempts < $maxAttempts) {
try {
$this->storage->put(
"uploads/{$uploadId}/chunk_{$chunkIndex}",
$data
);
return;
} catch (\Exception $e) {
$attempts++;
if ($attempts >= $maxAttempts) {
throw $e;
}
usleep(100000 * $attempts); // Exponential backoff
}
}
}
}
Upload State Lost
// Problem: Upload state not persisted
// Solution: Store upload state
final class UploadStateRepository
{
public function saveState(UploadState $state): void
{
$this->cache->set(
"upload_state:{$state->uploadId}",
serialize($state),
Duration::fromHours(24)
);
}
public function restoreState(string $uploadId): ?UploadState
{
$data = $this->cache->get("upload_state:{$uploadId}");
return $data ? unserialize($data) : null;
}
}
Fragment Rendering Issues
Symptoms
- Fragment not updating
- Entire component re-renders
- Focus lost after update
- Nested fragments broken
Diagnostic Steps
1. Verify Fragment Markup
<!-- Check fragment attribute -->
<div data-lc-fragment="section-name">
<!-- Content -->
</div>
2. Check Action Attribute
#[LiveAction]
#[Fragment('section-name')] // Must match HTML
public function updateSection(): void
{
// Update logic
}
3. Monitor Fragment Updates
window.addEventListener('livecomponent:fragment-updated', (e) => {
console.log('Fragment updated:', {
name: e.detail.fragmentName,
duration: e.detail.duration
});
});
Common Causes & Solutions
Fragment Name Mismatch
// Problem: Names don't match
#[Fragment('user-stats')] // ❌ kebab-case in PHP
public function updateStats(): void { }
<!-- userStats in HTML ❌ -->
<div data-lc-fragment="userStats">
// Solution: Use consistent naming
#[Fragment('user-stats')] // ✅ kebab-case
<div data-lc-fragment="user-stats"> <!-- ✅ Matches -->
Missing Fragment Attribute
// Problem: Fragment specified but no HTML marker
#[LiveAction]
#[Fragment('results')]
public function search(): void { }
// Template missing fragment marker
<div>
<!-- ❌ No data-lc-fragment="results" -->
<for items="results" as="result">
<div>{result.title}</div>
</for>
</div>
// Solution: Add fragment marker
<div data-lc-fragment="results"> <!-- ✅ Added -->
<for items="results" as="result">
<div>{result.title}</div>
</for>
</div>
Focus Lost After Update
// Problem: Input focus lost during fragment update
// Solution: Framework preserves focus automatically
// If not working, check:
// 1. Is input inside fragment?
<div data-lc-fragment="search">
<input type="text" id="search-input" /> ✅
</div>
// 2. Does input have stable ID?
<input type="text" id="search-input" /> ✅ Has ID
<input type="text" /> ❌ No ID
Browser Compatibility
Symptoms
- Works in Chrome but not Safari
- Mobile browser issues
- Older browser errors
Supported Browsers
- Chrome/Edge: 90+
- Firefox: 88+
- Safari: 14+
- Mobile Safari: 14+
- Mobile Chrome: 90+
Common Issues
ES2020 Features Not Supported
// Problem: Older browser lacks ES2020
// Solution: Build with polyfills
// vite.config.js
export default defineConfig({
build: {
target: 'es2015', // Broader compatibility
polyfillModulePreload: true
}
});
EventSource Not Supported
// Check SSE support
if (typeof EventSource === 'undefined') {
console.warn('SSE not supported - real-time updates disabled');
// Fallback to polling
}
iOS Safari Issues
// iOS Safari has stricter CORS requirements
// Ensure proper CORS headers
// PHP
header('Access-Control-Allow-Origin: https://yourdomain.com');
header('Access-Control-Allow-Credentials: true');
Debugging Tools
DevTools Panel
Enable DevTools:
LIVECOMPONENT_DEVTOOLS_ENABLED=true
Or via localStorage:
localStorage.setItem('livecomponent_devtools', 'true');
location.reload();
Features:
- Component tree inspection
- Action log with timing
- Event log
- Performance profiling
- Network monitoring
- DOM badges
Console Commands
// Get all components
LiveComponent.components
// Get specific component
const component = LiveComponent.getComponent('component-id');
// Inspect state
console.log(component.state);
// Execute action manually
LiveComponent.executeAction('component-id', 'actionName', {
param: 'value'
});
// Refresh component
LiveComponent.refresh('component-id');
// Get performance metrics
LiveComponent.getMetrics('component-id');
Server-Side Debugging
// Log component state
$this->logger->debug('Component state', [
'component' => static::class,
'state' => $this->getState()
]);
// Performance tracking
$start = hrtime(true);
$result = $this->expensiveOperation();
$duration = (hrtime(true) - $start) / 1e6;
$this->logger->info('Operation completed', [
'duration_ms' => $duration
]);
Network Debugging
F12 → Network Tab → Filter: livecomponent
Check:
- Request payloads
- Response times
- Error responses
- Batching efficiency
Getting Help
Before Asking for Help
- Check this guide for common solutions
- Enable DevTools and check console
- Reproduce in isolation (minimal example)
- Gather error messages (browser console + server logs)
Reporting Issues
Include:
- Browser + version
- PHP version
- Framework version
- Component code (minimal reproduction)
- Error messages
- Steps to reproduce
Resources
- Documentation: Getting Started, API Reference
- Examples:
src/Application/LiveComponents/ - Tests:
tests/e2e/livecomponents-*.spec.js - Security: security@example.com
- Bugs: GitHub Issues
Next: FAQ →