# LiveComponents Best Practices

Production-ready patterns and best practices for building scalable, maintainable LiveComponents.

## Table of Contents

- [Component Design](#component-design)
- [State Management](#state-management)
- [Performance Optimization](#performance-optimization)
- [Security Best Practices](#security-best-practices)
- [Testing Strategies](#testing-strategies)
- [Error Handling](#error-handling)
- [Accessibility](#accessibility)
- [Production Deployment](#production-deployment)

---

## Component Design

### Single Responsibility Principle

**✅ Good**: Component does one thing well
```php
// Focused component
final class SearchBox extends LiveComponent
{
    #[LiveProp(writable: true)]
    public string $query = '';

    #[LiveProp]
    public array $suggestions = [];

    #[LiveAction(debounce: 300)]
    public function search(): void
    {
        $this->suggestions = $this->searchService->getSuggestions($this->query);
    }
}
```

**❌ Bad**: Component tries to do everything
```php
// God component (anti-pattern)
final class Dashboard extends LiveComponent
{
    // User management
    #[LiveAction]
    public function createUser() { }
    #[LiveAction]
    public function deleteUser() { }

    // Analytics
    #[LiveAction]
    public function generateReport() { }

    // Settings
    #[LiveAction]
    public function updateSettings() { }

    // Notifications
    #[LiveAction]
    public function sendNotification() { }

    // ... 50 more actions
}
```

**Solution**: Split into focused components
```php
final class UserManagement extends LiveComponent { }
final class AnalyticsDashboard extends LiveComponent { }
final class SettingsPanel extends LiveComponent { }
final class NotificationCenter extends LiveComponent { }
```

### Component Composition

**✅ Good**: Compose components from smaller pieces
```html
<div data-lc-component="Dashboard">
    <include template="components/header" />

    <div class="grid">
        <include template="components/user-stats" data-lc-component="UserStats" />
        <include template="components/activity-feed" data-lc-component="ActivityFeed" />
        <include template="components/notifications" data-lc-component="Notifications" />
    </div>
</div>
```

**Benefits**:
- Reusable components
- Easier testing
- Better performance (independent updates)
- Clearer separation of concerns

### Keep Components Stateless When Possible

**✅ Good**: Stateless, reusable component
```php
final class UserCard extends LiveComponent
{
    #[LiveProp]
    public string $userId;

    #[LiveProp]
    public string $name;

    #[LiveProp]
    public string $avatar;

    // No mutable state, just display props
}
```

**❌ Bad**: Unnecessary internal state
```php
final class UserCard extends LiveComponent
{
    #[LiveProp]
    public string $userId;

    #[LiveProp]
    private bool $isExpanded = false; // Could be CSS-only

    #[LiveProp]
    private int $hoverCount = 0; // Useless tracking
}
```

### Use Value Objects

**✅ Good**: Type-safe with Value Objects
```php
final class OrderForm extends LiveComponent
{
    #[LiveProp]
    public Money $total;

    #[LiveProp]
    public Address $shippingAddress;

    #[LiveProp]
    public OrderStatus $status;
}
```

**❌ Bad**: Primitive obsession
```php
final class OrderForm extends LiveComponent
{
    #[LiveProp]
    public float $total; // No currency info

    #[LiveProp]
    public array $shippingAddress; // Unstructured

    #[LiveProp]
    public string $status; // No validation
}
```

---

## State Management

### Minimize State

**✅ Good**: Only essential state
```php
final class ProductList extends LiveComponent
{
    #[LiveProp]
    public array $filters = []; // User input

    #[LiveProp]
    public array $products = []; // Query results

    // Everything else computed from these two
}
```

**❌ Bad**: Redundant state
```php
final class ProductList extends LiveComponent
{
    #[LiveProp]
    public array $filters = [];

    #[LiveProp]
    public array $products = [];

    #[LiveProp]
    public int $totalProducts; // Redundant: count($products)

    #[LiveProp]
    public bool $hasProducts; // Redundant: !empty($products)

    #[LiveProp]
    public array $productIds; // Redundant: array_column($products, 'id')
}
```

### Computed Properties for Derived State

**✅ Good**: Compute derived values
```php
final class ShoppingCart extends LiveComponent
{
    #[LiveProp]
    public array $items = [];

    #[LiveProp(computed: true)]
    public function subtotal(): Money
    {
        return array_reduce(
            $this->items,
            fn($sum, $item) => $sum->add($item->price->multiply($item->quantity)),
            Money::zero()
        );
    }

    #[LiveProp(computed: true)]
    public function itemCount(): int
    {
        return array_sum(array_column($this->items, 'quantity'));
    }
}
```

### Normalize Complex State

**✅ Good**: Normalized state
```php
final class MessageList extends LiveComponent
{
    #[LiveProp]
    public array $messageIds = [1, 2, 3]; // Order preserved

    #[LiveProp]
    public array $messagesById = [ // Fast lookup
        1 => ['id' => 1, 'text' => '...'],
        2 => ['id' => 2, 'text' => '...'],
        3 => ['id' => 3, 'text' => '...'],
    ];

    #[LiveAction]
    public function deleteMessage(int $id): void
    {
        $this->messageIds = array_filter($this->messageIds, fn($msgId) => $msgId !== $id);
        unset($this->messagesById[$id]);
    }
}
```

**❌ Bad**: Nested arrays
```php
final class MessageList extends LiveComponent
{
    #[LiveProp]
    public array $messages = [
        ['id' => 1, 'text' => '...'],
        ['id' => 2, 'text' => '...'],
        ['id' => 3, 'text' => '...'],
    ];

    #[LiveAction]
    public function deleteMessage(int $id): void
    {
        // Slow O(n) search
        $this->messages = array_filter(
            $this->messages,
            fn($msg) => $msg['id'] !== $id
        );
    }
}
```

### Use Writable Props Sparingly

**✅ Good**: Writable only for user input
```php
final class ContactForm extends LiveComponent
{
    #[LiveProp(writable: true)]
    public string $name = '';

    #[LiveProp(writable: true)]
    public string $email = '';

    #[LiveProp] // Read-only
    public bool $submitted = false;
}
```

**❌ Bad**: Everything writable
```php
final class ContactForm extends LiveComponent
{
    #[LiveProp(writable: true)]
    public string $name = '';

    #[LiveProp(writable: true)]
    public bool $submitted = false; // Client could fake this

    #[LiveProp(writable: true)]
    public string $userId; // Security risk!
}
```

---

## Performance Optimization

### Use Fragments for Large Components

**✅ Good**: Fragment-based updates
```php
final class Dashboard extends LiveComponent
{
    #[LiveAction]
    #[Fragment('stats')]
    public function refreshStats(): void
    {
        // Only re-renders stats section
        $this->stats = $this->statsService->getLatest();
    }

    #[LiveAction]
    #[Fragment('activity')]
    public function refreshActivity(): void
    {
        // Only re-renders activity feed
        $this->activities = $this->activityService->getRecent();
    }
}
```

**Performance Impact**: 70-90% reduction in DOM updates

### Debounce Expensive Actions

**✅ Good**: Debounced search
```php
final class SearchBox extends LiveComponent
{
    #[LiveProp(writable: true)]
    public string $query = '';

    #[LiveAction(debounce: 300)]
    public function search(): void
    {
        // Waits 300ms after user stops typing
        $this->results = $this->searchService->search($this->query);
    }
}
```

**Performance Impact**: 80%+ reduction in API calls

### Cache Expensive Operations

**✅ Good**: Cached expensive query
```php
final class ReportViewer extends LiveComponent
{
    #[LiveAction]
    #[Cached(ttl: 1800, tags: ['reports'])]
    public function generateReport(): void
    {
        // Expensive operation cached for 30 minutes
        $this->reportData = $this->reportService->generate($this->reportId);
    }
}
```

### Lazy Load Non-Critical Data

**✅ Good**: Lazy loading
```php
final class UserProfile extends LiveComponent
{
    #[LiveProp]
    public string $userId;

    #[LiveProp(lazy: true)]
    public array $activityHistory = [];

    #[LiveAction]
    public function loadActivityHistory(): void
    {
        // Only loads when user requests it
        $this->activityHistory = $this->activityService->getForUser($this->userId);
    }
}
```

### Optimize Template Rendering

**✅ Good**: Efficient template
```html
<!-- Use fragments for independent sections -->
<div data-lc-component="ProductList">
    <div data-lc-fragment="filters">
        <!-- Filter UI -->
    </div>

    <div data-lc-fragment="products">
        <!-- Only this updates when products change -->
        <for items="products" as="product">
            <include template="components/product-card" data="{{ product }}" />
        </for>
    </div>
</div>
```

**❌ Bad**: Monolithic template
```html
<!-- Everything re-renders on any change -->
<div data-lc-component="ProductList">
    <!-- 10KB of HTML -->
    <div><!-- Filters --></div>
    <div><!-- Products --></div>
    <div><!-- Sidebar --></div>
    <div><!-- Footer --></div>
</div>
```

### Use Optimistic Updates for User Interactions

**✅ Good**: Instant feedback
```php
final class LikeButton extends LiveComponent
{
    #[LiveAction]
    #[Optimistic(updateProps: ['likes', 'isLiked'])]
    public function toggleLike(): void
    {
        // Client updates UI immediately (<50ms)
        // Server confirms asynchronously
        $this->isLiked = !$this->isLiked;
        $this->likes += $this->isLiked ? 1 : -1;
    }
}
```

---

## Security Best Practices

### Rate Limit All Public Actions

**✅ Good**: Protected endpoints
```php
final class ContactForm extends LiveComponent
{
    #[LiveAction]
    #[RateLimit(requests: 3, window: 3600, key: 'ip')]
    public function submitForm(array $data): void
    {
        // Max 3 submissions per hour per IP
        $this->emailService->send($data);
    }
}
```

### Authorize Sensitive Actions

**✅ Good**: Protected actions
```php
final class UserManagement extends LiveComponent
{
    #[LiveAction]
    #[Authorize(roles: ['admin'])]
    #[RateLimit(requests: 10, window: 60, key: 'user')]
    public function deleteUser(string $userId): void
    {
        // Only admins can delete, max 10 per minute
        $this->userService->delete($userId);
    }
}
```

### Use Idempotency for Critical Actions

**✅ Good**: Idempotent operations
```php
final class PaymentForm extends LiveComponent
{
    #[LiveAction]
    #[Idempotent(ttl: 86400)]
    #[Authorize(permissions: ['payments.process'])]
    public function processPayment(string $idempotencyKey, array $paymentData): void
    {
        // Duplicate requests return cached result
        // Prevents double-charging
        $this->paymentService->charge($paymentData);
    }
}
```

### Validate All Input

**✅ Good**: Server-side validation
```php
final class RegistrationForm extends LiveComponent
{
    #[LiveAction]
    #[Validated(rules: [
        'email' => ['required', 'email', 'unique:users'],
        'password' => ['required', 'min:8', 'confirmed'],
    ])]
    #[RateLimit(requests: 5, window: 3600, key: 'ip')]
    public function register(string $email, string $password): void
    {
        // Input validated before execution
        $this->authService->register($email, $password);
    }
}
```

### Encrypt Sensitive Data

**✅ Good**: Encrypted sensitive props
```php
final class PaymentForm extends LiveComponent
{
    #[LiveProp]
    #[Encrypted]
    public string $creditCardNumber = '';

    #[LiveProp]
    #[Encrypted]
    public string $cvv = '';

    #[LiveAction]
    #[Idempotent]
    #[Authorize(permissions: ['payments.process'])]
    public function processPayment(): void
    {
        // Properties automatically decrypted server-side
        $this->paymentService->charge($this->creditCardNumber, $this->cvv);
    }
}
```

### Never Trust Client State

**✅ Good**: Validate server-side
```php
final class CheckoutForm extends LiveComponent
{
    #[LiveProp(writable: true)]
    public array $cartItems = [];

    #[LiveAction]
    public function checkout(): void
    {
        // ALWAYS recalculate totals server-side
        $total = $this->cartService->calculateTotal($this->cartItems);

        // Don't trust client-provided total
        $this->paymentService->charge($total);
    }
}
```

**❌ Bad**: Trust client state
```php
final class CheckoutForm extends LiveComponent
{
    #[LiveProp(writable: true)]
    public float $total; // Client could modify this!

    #[LiveAction]
    public function checkout(): void
    {
        // Dangerous: charging client-provided amount
        $this->paymentService->charge($this->total);
    }
}
```

---

## Testing Strategies

### Unit Test Component Logic

**✅ Good**: Test business logic
```php
it('calculates cart total correctly', function () {
    $cart = new ShoppingCart();
    $cart->items = [
        new CartItem(price: Money::fromCents(1000), quantity: 2),
        new CartItem(price: Money::fromCents(500), quantity: 1),
    ];

    expect($cart->subtotal()->toCents())->toBe(2500);
});
```

### Integration Test Actions

**✅ Good**: Test action execution
```php
it('adds item to cart', function () {
    $cart = new ShoppingCart();

    $cart->addItem(productId: '123', quantity: 2);

    expect($cart->items)->toHaveCount(1);
    expect($cart->items[0]->productId)->toBe('123');
    expect($cart->items[0]->quantity)->toBe(2);
});
```

### E2E Test User Workflows

**✅ Good**: Test complete flows
```javascript
test('user can complete checkout', async ({ page }) => {
    // Add product to cart
    await page.goto('/products/123');
    await page.click('[data-lc-action="addToCart"]');

    // Go to cart
    await page.goto('/cart');
    await expect(page.locator('.cart-item')).toHaveCount(1);

    // Proceed to checkout
    await page.click('[data-lc-action="checkout"]');

    // Fill payment form
    await page.fill('[name="cardNumber"]', '4242424242424242');
    await page.fill('[name="cvv"]', '123');

    // Submit
    await page.click('[data-lc-action="processPayment"]');

    // Verify success
    await expect(page).toHaveURL('/order/confirmation');
});
```

### Test Fragment Updates

**✅ Good**: Verify partial rendering
```javascript
test('updates only search results fragment', async ({ page }) => {
    const headerHtml = await page.locator('[data-lc-fragment="header"]').innerHTML();

    await page.fill('[data-lc-model="searchQuery"]', 'test');
    await page.click('[data-lc-action="search"]');

    await page.waitForResponse(r => r.url().includes('/livecomponent'));

    // Header unchanged
    const newHeaderHtml = await page.locator('[data-lc-fragment="header"]').innerHTML();
    expect(newHeaderHtml).toBe(headerHtml);

    // Results updated
    await expect(page.locator('[data-lc-fragment="results"] .result-item')).toHaveCount(5);
});
```

### Test Error Handling

**✅ Good**: Test failure scenarios
```php
it('handles validation errors', function () {
    $form = new RegistrationForm();

    expect(fn() => $form->register('invalid-email', 'short'))
        ->toThrow(ValidationException::class);

    expect($form->errors)->toHaveKey('email');
    expect($form->errors)->toHaveKey('password');
});
```

---

## Error Handling

### Provide Clear Error Messages

**✅ Good**: User-friendly errors
```php
final class PaymentForm extends LiveComponent
{
    #[LiveAction]
    public function processPayment(array $paymentData): void
    {
        try {
            $this->paymentService->charge($paymentData);
            $this->success = true;
        } catch (InsufficientFundsException $e) {
            $this->error = 'Insufficient funds. Please try a different payment method.';
        } catch (CardDeclinedException $e) {
            $this->error = 'Your card was declined. Please contact your bank.';
        } catch (PaymentGatewayException $e) {
            $this->error = 'Payment processing is temporarily unavailable. Please try again later.';
            $this->logger->error('Payment gateway error', ['exception' => $e]);
        }
    }
}
```

**❌ Bad**: Technical error messages
```php
catch (\Exception $e) {
    $this->error = $e->getMessage(); // "SQLSTATE[HY000]..."
}
```

### Handle Network Failures Gracefully

**✅ Good**: Retry with backoff
```javascript
window.addEventListener('livecomponent:request-failed', async (event) => {
    const { componentId, requestId, error } = event.detail;

    if (error.status === 0) {
        // Network error - retry with exponential backoff
        let delay = 1000;
        for (let i = 0; i < 3; i++) {
            await new Promise(resolve => setTimeout(resolve, delay));

            try {
                await retryRequest(componentId, requestId);
                break; // Success
            } catch (e) {
                delay *= 2;
            }
        }
    }
});
```

### Validate Input Early

**✅ Good**: Client-side + server-side validation
```html
<form data-lc-action="register">
    <input
        type="email"
        name="email"
        required
        pattern="[^@]+@[^@]+\.[^@]+"
        data-lc-model="email"
    />

    <input
        type="password"
        name="password"
        required
        minlength="8"
        data-lc-model="password"
    />

    <button type="submit">Register</button>
</form>
```

```php
#[LiveAction]
#[Validated(rules: [
    'email' => ['required', 'email', 'unique:users'],
    'password' => ['required', 'min:8'],
])]
public function register(string $email, string $password): void
{
    // Server-side validation happens first
    $this->authService->register($email, $password);
}
```

---

## Accessibility

### Use Semantic HTML

**✅ Good**: Semantic markup
```html
<nav aria-label="Main navigation" data-lc-component="Navigation">
    <ul role="list">
        <for items="menuItems" as="item">
            <li>
                <a
                    href="{{ item.url }}"
                    aria-current="{{ item.isActive ? 'page' : null }}"
                >
                    {{ item.label }}
                </a>
            </li>
        </for>
    </ul>
</nav>
```

**❌ Bad**: Divs everywhere
```html
<div data-lc-component="Navigation">
    <div onclick="navigate()">Home</div>
    <div onclick="navigate()">About</div>
</div>
```

### Provide ARIA Labels

**✅ Good**: Accessible labels
```html
<button
    data-lc-action="deleteItem"
    data-lc-params='{"id": "{{ item.id }}"}'
    aria-label="Delete {{ item.name }}"
>
    ✕
</button>

<div
    data-lc-fragment="loading"
    role="status"
    aria-live="polite"
    aria-busy="{{ isLoading }}"
>
    <if condition="isLoading">
        Loading...
    </if>
</div>
```

### Announce Dynamic Updates

**✅ Good**: Screen reader announcements
```html
<div role="status" aria-live="polite" aria-atomic="true">
    <if condition="successMessage">
        {{ successMessage }}
    </if>
</div>

<div role="alert" aria-live="assertive">
    <if condition="errorMessage">
        {{ errorMessage }}
    </if>
</div>
```

### Keyboard Navigation

**✅ Good**: Keyboard accessible
```html
<div
    data-lc-component="Dropdown"
    role="combobox"
    aria-expanded="{{ isOpen }}"
    aria-controls="dropdown-list"
>
    <button
        data-lc-action="toggle"
        aria-haspopup="listbox"
        onkeydown="handleKeyDown(event)"
    >
        {{ selectedOption }}
    </button>

    <ul
        id="dropdown-list"
        role="listbox"
        aria-label="Options"
        style="display: {{ isOpen ? 'block' : 'none' }}"
    >
        <for items="options" as="option">
            <li
                role="option"
                tabindex="0"
                data-lc-action="selectOption"
                data-lc-params='{"value": "{{ option.value }}"}'
                onkeydown="handleOptionKeyDown(event)"
            >
                {{ option.label }}
            </li>
        </for>
    </ul>
</div>
```

---

## Production Deployment

### Environment Configuration

**✅ Production `.env`**:
```env
# LiveComponents Configuration
LIVECOMPONENT_ENABLED=true
LIVECOMPONENT_CSRF_PROTECTION=true
LIVECOMPONENT_RATE_LIMIT=60
LIVECOMPONENT_DEVTOOLS_ENABLED=false

# Security
LIVECOMPONENT_ENCRYPTION_KEY=your-256-bit-encryption-key-here
CSRF_TOKEN_LENGTH=32

# Performance
LIVECOMPONENT_BATCH_SIZE=10
LIVECOMPONENT_BATCH_DEBOUNCE=50
LIVECOMPONENT_CACHE_ENABLED=true

# SSE
LIVECOMPONENT_SSE_ENABLED=true
LIVECOMPONENT_SSE_HEARTBEAT_INTERVAL=30
```

### Production Checklist

**Before Deploying**:
- [ ] Generate new `LIVECOMPONENT_ENCRYPTION_KEY`
- [ ] Disable DevTools (`LIVECOMPONENT_DEVTOOLS_ENABLED=false`)
- [ ] Enable CSRF protection
- [ ] Configure rate limits for all public actions
- [ ] Test all critical user flows
- [ ] Run security audit
- [ ] Test with real-world data volumes
- [ ] Configure caching
- [ ] Set up error monitoring
- [ ] Test SSE connections (HTTPS required)
- [ ] Verify fragment updates work correctly
- [ ] Test file uploads with large files
- [ ] Test error handling and recovery
- [ ] Validate accessibility (WCAG 2.1 AA)

### Monitor Performance

**✅ Good**: Track metrics
```javascript
window.addEventListener('livecomponent:action-executed', (event) => {
    const { action, duration } = event.detail;

    // Send to analytics
    analytics.track('livecomponent_performance', {
        action: action,
        duration: duration,
        p95: calculateP95(action, duration),
        p99: calculateP99(action, duration)
    });

    // Alert on slow actions
    if (duration > 1000) {
        errorReporter.captureMessage(`Slow action: ${action} (${duration}ms)`);
    }
});
```

### Error Tracking

**✅ Good**: Comprehensive error tracking
```javascript
window.addEventListener('livecomponent:error', (event) => {
    const { componentId, error, context } = event.detail;

    errorReporter.captureException(error, {
        tags: {
            component: componentId,
            context: context
        },
        extra: {
            props: LiveComponent.getComponent(componentId)?.props,
            user_agent: navigator.userAgent
        }
    });
});
```

### Performance Budgets

**Set and Monitor**:
- Action latency: p95 < 100ms, p99 < 250ms
- Fragment updates: < 50ms
- Network efficiency: > 80% (via batching)
- Memory usage: < 2MB per component
- Bundle size: < 50KB (gzipped)

---

## Summary: The Golden Rules

1. **Keep components focused** - Single responsibility
2. **Minimize state** - Compute derived values
3. **Use fragments** - For large components (>5KB)
4. **Debounce expensive actions** - Search, API calls
5. **Cache when possible** - Expensive queries
6. **Rate limit everything** - Public endpoints
7. **Authorize sensitive actions** - Admin operations
8. **Validate all input** - Client + server
9. **Encrypt sensitive data** - PII, financial
10. **Test thoroughly** - Unit, integration, E2E
11. **Handle errors gracefully** - User-friendly messages
12. **Make it accessible** - WCAG 2.1 AA
13. **Monitor production** - Performance + errors
14. **Use optimistic updates** - Instant feedback
15. **Document your code** - Future you will thank you

---

For more details, see:
- [Performance Guide](performance-guide.md)
- [Security Guide](security-guide.md)
- [Testing Guide](../testing/README.md)
- [API Reference](api-reference.md)
