# LiveComponents Attributes Reference
Complete reference for all LiveComponents attributes with examples and best practices.
## Table of Contents
- [Component Attributes](#component-attributes)
- [@LiveProp](#liveprop)
- [@LiveAction](#liveaction)
- [@Fragment](#fragment)
- [Security Attributes](#security-attributes)
- [@RateLimit](#ratelimit)
- [@Authorize](#authorize)
- [@Idempotent](#idempotent)
- [@Encrypted](#encrypted)
- [Performance Attributes](#performance-attributes)
- [@Optimistic](#optimistic)
- [@Cached](#cached)
- [@NoBatch](#nobatch)
- [Validation Attributes](#validation-attributes)
- [@Validated](#validated)
- [State Management Attributes](#state-management-attributes)
- [@Persisted](#persisted)
---
## Component Attributes
### @LiveProp
**Purpose**: Mark a component property as reactive - automatically synchronized between server and client.
**Signature**:
```php
#[LiveProp(
writable: bool = false, // Allow client-side updates
lazy: bool = false, // Load on first access
computed: bool = false, // Computed from other props
dehydrate: ?callable = null, // Custom serialization
hydrate: ?callable = null // Custom deserialization
)]
```
**Basic Usage**:
```php
final class Counter extends LiveComponent
{
#[LiveProp]
public int $count = 0;
#[LiveProp(writable: true)]
public string $searchQuery = '';
#[LiveProp(lazy: true)]
public array $expensiveData = [];
#[LiveAction]
public function increment(): void
{
$this->count++;
}
}
```
**Writable Props** (Two-Way Binding):
```php
final class SearchForm extends LiveComponent
{
#[LiveProp(writable: true)]
public string $query = '';
#[LiveProp(writable: true)]
public array $filters = [];
#[LiveAction]
public function search(): void
{
// $this->query automatically updated from client
$this->results = $this->searchService->search($this->query, $this->filters);
}
}
```
**HTML Template**:
```html
```
**Lazy Props** (Load on Demand):
```php
final class Dashboard extends LiveComponent
{
#[LiveProp]
public string $userId;
#[LiveProp(lazy: true)]
public array $analytics = [];
#[LiveAction]
public function loadAnalytics(): void
{
// Only load when explicitly requested
$this->analytics = $this->analyticsService->getForUser($this->userId);
}
}
```
**Computed Props**:
```php
final class ShoppingCart extends LiveComponent
{
#[LiveProp]
public array $items = [];
#[LiveProp(computed: true)]
public function total(): Money
{
return array_reduce(
$this->items,
fn($total, $item) => $total->add($item->price),
Money::zero()
);
}
}
```
**Custom Serialization**:
```php
final class DateRangePicker extends LiveComponent
{
#[LiveProp(
dehydrate: [self::class, 'dehydrateDate'],
hydrate: [self::class, 'hydrateDate']
)]
public \DateTimeImmutable $startDate;
public static function dehydrateDate(\DateTimeImmutable $date): string
{
return $date->format('Y-m-d');
}
public static function hydrateDate(string $date): \DateTimeImmutable
{
return new \DateTimeImmutable($date);
}
}
```
**Best Practices**:
- ✅ Use `writable: true` only for user input fields
- ✅ Use `lazy: true` for expensive data that's not always needed
- ✅ Keep props serializable (primitives, arrays, or custom dehydrate/hydrate)
- ❌ Don't make service dependencies `#[LiveProp]`
- ❌ Don't expose sensitive data as LiveProps
**Gotchas**:
- Writable props validate on server before updating
- Computed props are read-only on client
- Lazy props won't be available until explicitly loaded
---
### @LiveAction
**Purpose**: Mark a method as a server-side action that can be triggered from the client.
**Signature**:
```php
#[LiveAction(
name: ?string = null, // Custom action name
debounce: ?int = null, // Debounce in milliseconds
throttle: ?int = null, // Throttle in milliseconds
confirm: ?string = null // Confirmation message
)]
```
**Basic Usage**:
```php
final class TodoList extends LiveComponent
{
#[LiveProp]
public array $todos = [];
#[LiveAction]
public function addTodo(string $text): void
{
$this->todos[] = [
'id' => uniqid(),
'text' => $text,
'done' => false
];
}
#[LiveAction]
public function toggleTodo(string $id): void
{
foreach ($this->todos as &$todo) {
if ($todo['id'] === $id) {
$todo['done'] = !$todo['done'];
}
}
}
#[LiveAction]
public function removeTodo(string $id): void
{
$this->todos = array_filter(
$this->todos,
fn($todo) => $todo['id'] !== $id
);
}
}
```
**HTML Template**:
```html
{{ todo.text }}
```
**Debounced Actions** (Search Input):
```php
final class SearchComponent extends LiveComponent
{
#[LiveProp(writable: true)]
public string $query = '';
#[LiveProp]
public array $results = [];
#[LiveAction(debounce: 300)]
public function search(): void
{
// Only executes 300ms after user stops typing
$this->results = $this->searchService->search($this->query);
}
}
```
**Throttled Actions** (Rate Limited):
```php
final class AnalyticsTracker extends LiveComponent
{
#[LiveAction(throttle: 1000)]
public function trackEvent(string $eventName): void
{
// Maximum once per second
$this->analyticsService->track($eventName);
}
}
```
**Confirmation Actions**:
```php
final class UserManager extends LiveComponent
{
#[LiveAction(confirm: 'Are you sure you want to delete this user?')]
public function deleteUser(string $userId): void
{
$this->userService->delete($userId);
}
}
```
**Custom Action Names**:
```php
final class PaymentForm extends LiveComponent
{
#[LiveAction(name: 'submit-payment')]
public function processPayment(array $paymentData): void
{
$this->paymentService->process($paymentData);
}
}
```
**Best Practices**:
- ✅ Use debounce for search/autocomplete
- ✅ Use throttle for tracking/analytics
- ✅ Use confirm for destructive actions
- ✅ Keep actions focused (single responsibility)
- ❌ Don't perform long-running operations (use jobs)
- ❌ Don't return data (update LiveProps instead)
**Gotchas**:
- Actions automatically re-render component by default
- Debounce/throttle applied client-side before request
- Confirmation shows native browser confirm dialog
---
### @Fragment
**Purpose**: Mark an action to update only specific fragments of the component (partial rendering).
**Signature**:
```php
#[Fragment(
string|array $names // Fragment name(s) to update
)]
```
**Basic Usage**:
```php
final class Dashboard extends LiveComponent
{
#[LiveProp]
public array $stats = [];
#[LiveProp]
public array $notifications = [];
#[LiveAction]
#[Fragment('stats')]
public function refreshStats(): void
{
// Only re-renders the 'stats' fragment
$this->stats = $this->statsService->getLatest();
}
#[LiveAction]
#[Fragment('notifications')]
public function refreshNotifications(): void
{
// Only re-renders the 'notifications' fragment
$this->notifications = $this->notificationService->getUnread();
}
#[LiveAction]
#[Fragment(['stats', 'notifications'])]
public function refreshAll(): void
{
// Re-renders both fragments
$this->stats = $this->statsService->getLatest();
$this->notifications = $this->notificationService->getUnread();
}
}
```
**HTML Template**:
```html
Statistics
{{ stat.label }}: {{ stat.value }}
Notifications
{{ notification.message }}
```
**Nested Fragments**:
```php
final class ProductList extends LiveComponent
{
#[LiveProp]
public array $products = [];
#[LiveProp]
public array $filters = [];
#[LiveAction]
#[Fragment('product-list')]
public function applyFilter(string $category): void
{
$this->filters['category'] = $category;
$this->products = $this->productService->search($this->filters);
}
#[LiveAction]
#[Fragment('product-item')]
public function toggleFavorite(string $productId): void
{
$this->productService->toggleFavorite($productId);
}
}
```
**HTML Template with Nested Fragments**:
```html
{{ product.name }}
```
**Performance Benefits**:
```php
// Without fragments (full re-render)
#[LiveAction]
public function updateSmallPart(): void
{
// Re-renders entire 10KB template
}
// With fragments (partial re-render)
#[LiveAction]
#[Fragment('small-part')]
public function updateSmallPart(): void
{
// Only re-renders 500 bytes
// 95% network reduction!
}
```
**Best Practices**:
- ✅ Use fragments for large components with independent sections
- ✅ Name fragments semantically (`user-profile`, not `fragment-1`)
- ✅ Keep fragments independent (minimal cross-dependencies)
- ✅ Use nested fragments for list items
- ❌ Don't create too many small fragments (overhead)
- ❌ Don't use fragments for tiny components (< 1KB)
**Gotchas**:
- Fragment names must match exactly between PHP and HTML
- Nested fragments require unique identifiers (e.g., `data-product-id`)
- Missing fragment in template causes full re-render
---
## Security Attributes
### @RateLimit
**Purpose**: Limit the rate at which an action can be executed to prevent abuse.
**Signature**:
```php
#[RateLimit(
int $requests, // Number of allowed requests
int $window, // Time window in seconds
string $key = 'ip' // Rate limit key ('ip', 'user', 'component')
)]
```
**IP-Based Rate Limiting**:
```php
final class ContactForm extends LiveComponent
{
#[LiveAction]
#[RateLimit(requests: 3, window: 3600, key: 'ip')]
public function submitForm(array $formData): void
{
// Max 3 submissions per hour per IP address
$this->emailService->send($formData);
}
}
```
**User-Based Rate Limiting**:
```php
final class ApiExplorer extends LiveComponent
{
#[LiveAction]
#[RateLimit(requests: 100, window: 60, key: 'user')]
public function executeQuery(string $query): void
{
// Max 100 queries per minute per authenticated user
$this->results = $this->apiService->query($query);
}
}
```
**Component-Based Rate Limiting**:
```php
final class ExpensiveReport extends LiveComponent
{
#[LiveAction]
#[RateLimit(requests: 5, window: 300, key: 'component')]
public function generateReport(): void
{
// Max 5 reports per 5 minutes for this component instance
$this->report = $this->reportService->generate();
}
}
```
**Multiple Rate Limits**:
```php
final class PaymentForm extends LiveComponent
{
#[LiveAction]
#[RateLimit(requests: 3, window: 60, key: 'ip')]
#[RateLimit(requests: 10, window: 3600, key: 'user')]
public function processPayment(array $paymentData): void
{
// IP: Max 3 per minute
// User: Max 10 per hour
$this->paymentService->process($paymentData);
}
}
```
**Best Practices**:
- ✅ Use `key: 'ip'` for public endpoints (contact forms, registrations)
- ✅ Use `key: 'user'` for authenticated endpoints
- ✅ Use `key: 'component'` for expensive operations
- ✅ Set generous limits to avoid false positives
- ❌ Don't rate limit critical user flows too aggressively
**Gotchas**:
- Rate limit exceeded returns 429 Too Many Requests
- Client automatically shows retry-after countdown
- Rate limits are per-action (not per-component)
---
### @Authorize
**Purpose**: Restrict action execution to users with specific roles or permissions.
**Signature**:
```php
#[Authorize(
array $roles = [], // Required roles (any match)
array $permissions = [], // Required permissions (all match)
bool $requireAll = false // Require all roles (default: any)
)]
```
**Role-Based Authorization**:
```php
final class UserManagement extends LiveComponent
{
#[LiveAction]
#[Authorize(roles: ['admin'])]
public function deleteUser(string $userId): void
{
// Only admins can delete users
$this->userService->delete($userId);
}
#[LiveAction]
#[Authorize(roles: ['admin', 'moderator'])]
public function banUser(string $userId): void
{
// Admins OR moderators can ban
$this->userService->ban($userId);
}
}
```
**Permission-Based Authorization**:
```php
final class DocumentEditor extends LiveComponent
{
#[LiveAction]
#[Authorize(permissions: ['documents.edit'])]
public function saveDocument(string $content): void
{
$this->documentService->save($content);
}
#[LiveAction]
#[Authorize(permissions: ['documents.delete'])]
public function deleteDocument(string $documentId): void
{
$this->documentService->delete($documentId);
}
}
```
**Combined Authorization**:
```php
final class BillingPanel extends LiveComponent
{
#[LiveAction]
#[Authorize(
roles: ['admin', 'billing'],
permissions: ['billing.process-refund'],
requireAll: true
)]
public function processRefund(string $orderId): void
{
// Must have (admin OR billing role) AND billing.process-refund permission
$this->billingService->refund($orderId);
}
}
```
**Dynamic Authorization**:
```php
final class ProjectEditor extends LiveComponent
{
#[LiveProp]
public string $projectId;
#[LiveAction]
public function saveProject(array $data): void
{
// Custom authorization logic
if (!$this->authService->canEditProject($this->projectId)) {
throw new UnauthorizedException('You cannot edit this project');
}
$this->projectService->save($this->projectId, $data);
}
}
```
**Best Practices**:
- ✅ Use roles for broad access control
- ✅ Use permissions for fine-grained control
- ✅ Combine roles and permissions for complex requirements
- ✅ Show/hide UI elements based on authorization
- ❌ Don't rely solely on client-side authorization
**Gotchas**:
- Authorization failure returns 403 Forbidden
- Unauthorized actions don't execute (state unchanged)
- Client-side UI should match server-side authorization
---
### @Idempotent
**Purpose**: Ensure an action can be safely retried without duplicate side effects.
**Signature**:
```php
#[Idempotent(
string $keyParam = 'idempotencyKey', // Parameter name for idempotency key
int $ttl = 3600 // TTL in seconds (default: 1 hour)
)]
```
**Basic Usage**:
```php
final class OrderForm extends LiveComponent
{
#[LiveAction]
#[Idempotent]
public function submitOrder(string $idempotencyKey, array $orderData): void
{
// Duplicate requests with same idempotencyKey return cached result
$order = $this->orderService->create($orderData);
$this->orderId = $order->id;
}
}
```
**HTML Template**:
```html
```
**Custom Key Parameter**:
```php
final class PaymentProcessor extends LiveComponent
{
#[LiveAction]
#[Idempotent(keyParam: 'transactionId', ttl: 7200)]
public function processPayment(string $transactionId, array $paymentData): void
{
// Uses 'transactionId' as idempotency key
// Cached for 2 hours
$this->paymentService->process($transactionId, $paymentData);
}
}
```
**Use Cases**:
- ✅ Order submissions
- ✅ Payment processing
- ✅ Email sending
- ✅ API calls to external services
- ✅ Database writes
**Best Practices**:
- ✅ Use for all non-idempotent operations (CREATE, DELETE)
- ✅ Use longer TTL for critical operations (payments: 24 hours)
- ✅ Generate idempotency keys client-side (UUID v4)
- ❌ Don't use for read-only operations (unnecessary overhead)
**Gotchas**:
- Idempotent actions return cached response on retry
- TTL determines how long duplicates are prevented
- Client must provide idempotency key (automatic with `data-lc-idempotency`)
---
### @Encrypted
**Purpose**: Automatically encrypt and decrypt sensitive LiveProp values.
**Signature**:
```php
#[Encrypted(
string $algorithm = 'aes-256-gcm' // Encryption algorithm
)]
```
**Basic Usage**:
```php
final class PaymentForm extends LiveComponent
{
#[LiveProp]
public string $orderId;
#[LiveProp]
#[Encrypted]
public string $creditCardNumber = '';
#[LiveProp]
#[Encrypted]
public string $cvv = '';
#[LiveAction]
public function processPayment(): void
{
// Properties automatically decrypted here
$this->paymentService->charge(
$this->creditCardNumber,
$this->cvv
);
}
}
```
**Multiple Encrypted Props**:
```php
final class UserProfile extends LiveComponent
{
#[LiveProp]
public string $userId;
#[LiveProp]
#[Encrypted]
public string $ssn = '';
#[LiveProp]
#[Encrypted]
public string $bankAccount = '';
#[LiveProp]
#[Encrypted]
public array $sensitiveNotes = [];
}
```
**Best Practices**:
- ✅ Encrypt all PII (personally identifiable information)
- ✅ Encrypt financial data (credit cards, bank accounts)
- ✅ Encrypt authentication tokens
- ✅ Use in combination with HTTPS
- ❌ Don't encrypt non-sensitive data (performance overhead)
- ❌ Don't rely solely on encryption (validate and sanitize)
**Gotchas**:
- Encrypted props increase payload size (~30%)
- Encryption/decryption adds ~5ms latency
- Requires LIVECOMPONENT_ENCRYPTION_KEY in .env
---
## Performance Attributes
### @Optimistic
**Purpose**: Configure optimistic UI updates for instant perceived performance.
**Signature**:
```php
#[Optimistic(
bool $enabled = true, // Enable optimistic updates
array $updateProps = [], // Props to update optimistically
?string $rollbackOn = null // Rollback condition
)]
```
**Basic Usage**:
```php
final class LikeButton extends LiveComponent
{
#[LiveProp]
public int $likes = 0;
#[LiveProp]
public bool $isLiked = false;
#[LiveAction]
#[Optimistic(updateProps: ['likes', 'isLiked'])]
public function toggleLike(): void
{
// Client updates UI immediately
// Server confirms asynchronously
if ($this->isLiked) {
$this->likes--;
$this->isLiked = false;
} else {
$this->likes++;
$this->isLiked = true;
}
}
}
```
**HTML Template**:
```html
```
**Conditional Rollback**:
```php
final class InventoryManager extends LiveComponent
{
#[LiveProp]
public int $stock = 100;
#[LiveAction]
#[Optimistic(
updateProps: ['stock'],
rollbackOn: 'stock < 0'
)]
public function reserveItem(int $quantity): void
{
if ($this->stock < $quantity) {
throw new InsufficientStockException();
}
$this->stock -= $quantity;
}
}
```
**Best Practices**:
- ✅ Use for user interactions (likes, votes, favorites)
- ✅ Use for non-critical updates
- ✅ Provide visual feedback for rollbacks
- ❌ Don't use for financial transactions
- ❌ Don't use for destructive actions
**Gotchas**:
- Optimistic updates happen instantly (<50ms)
- Server validation can trigger rollback
- Network failures automatically rollback
---
### @Cached
**Purpose**: Cache action results to improve performance for expensive operations.
**Signature**:
```php
#[Cached(
int $ttl = 3600, // Cache TTL in seconds
string $key = 'auto', // Cache key strategy
array $tags = [] // Cache tags for invalidation
)]
```
**Basic Usage**:
```php
final class ReportViewer extends LiveComponent
{
#[LiveProp]
public string $reportId;
#[LiveProp]
public array $reportData = [];
#[LiveAction]
#[Cached(ttl: 1800)]
public function loadReport(): void
{
// Expensive operation cached for 30 minutes
$this->reportData = $this->reportService->generate($this->reportId);
}
}
```
**Tagged Caching**:
```php
final class ProductCatalog extends LiveComponent
{
#[LiveProp]
public string $categoryId;
#[LiveProp]
public array $products = [];
#[LiveAction]
#[Cached(ttl: 3600, tags: ['products', 'category:{categoryId}'])]
public function loadProducts(): void
{
$this->products = $this->productService->getByCategory($this->categoryId);
}
#[LiveAction]
public function invalidateCache(): void
{
// Invalidate all product caches
$this->cache->invalidateTags(['products']);
}
}
```
**Custom Cache Keys**:
```php
final class UserDashboard extends LiveComponent
{
#[LiveProp]
public string $userId;
#[LiveAction]
#[Cached(ttl: 600, key: 'user:{userId}:dashboard')]
public function loadDashboard(): void
{
// Cache key: "user:123:dashboard"
$this->dashboardData = $this->dashboardService->getForUser($this->userId);
}
}
```
**Best Practices**:
- ✅ Cache expensive database queries
- ✅ Cache external API calls
- ✅ Use tags for batch invalidation
- ✅ Set appropriate TTL based on data freshness needs
- ❌ Don't cache user-specific sensitive data
- ❌ Don't cache highly dynamic data
**Gotchas**:
- Cache invalidation is eventual (not immediate)
- Tagged invalidation requires cache driver support
- Cache misses execute full action
---
### @NoBatch
**Purpose**: Disable automatic request batching for an action.
**Signature**:
```php
#[NoBatch]
```
**Basic Usage**:
```php
final class FileUploader extends LiveComponent
{
#[LiveAction]
#[NoBatch]
public function uploadFile(UploadedFile $file): void
{
// File uploads should not be batched
$this->uploadedFileId = $this->uploadService->store($file);
}
#[LiveAction]
public function deleteFile(string $fileId): void
{
// Regular action - can be batched
$this->uploadService->delete($fileId);
}
}
```
**Use Cases**:
- ✅ File uploads
- ✅ Real-time operations
- ✅ Time-sensitive actions
- ✅ Operations requiring immediate response
**Best Practices**:
- ✅ Use for operations that can't be delayed
- ✅ Use for operations with large payloads
- ❌ Don't overuse (batching improves performance)
**Gotchas**:
- Disables batching only for this specific action
- Other actions in the same component can still batch
- Increases number of HTTP requests
---
## Validation Attributes
### @Validated
**Purpose**: Automatically validate action parameters before execution.
**Signature**:
```php
#[Validated(
array $rules = [], // Validation rules per parameter
?string $errorFragment = null // Fragment to update on validation error
)]
```
**Basic Usage**:
```php
final class RegistrationForm extends LiveComponent
{
#[LiveProp]
public array $errors = [];
#[LiveAction]
#[Validated(
rules: [
'email' => ['required', 'email', 'unique:users'],
'password' => ['required', 'min:8', 'confirmed'],
'username' => ['required', 'alphanumeric', 'min:3', 'max:20']
],
errorFragment: 'form-errors'
)]
public function register(string $email, string $password, string $username): void
{
// Parameters automatically validated
$user = $this->authService->register($email, $password, $username);
$this->redirect('/dashboard');
}
}
```
**HTML Template**:
```html
Please correct the errors above.
```
**Available Validation Rules**:
- `required` - Field must be present and non-empty
- `email` - Valid email format
- `min:n` - Minimum length/value
- `max:n` - Maximum length/value
- `numeric` - Numeric value
- `alphanumeric` - Alphanumeric characters only
- `unique:table,column` - Unique in database
- `exists:table,column` - Exists in database
- `confirmed` - Match confirmation field (password_confirmation)
- `regex:pattern` - Match regex pattern
- `in:foo,bar,baz` - Value in list
- `url` - Valid URL format
- `date` - Valid date format
**Custom Validation**:
```php
final class ProductForm extends LiveComponent
{
#[LiveAction]
public function saveProduct(array $productData): void
{
// Custom validation logic
$validator = new ProductValidator();
if (!$validator->validate($productData)) {
$this->errors = $validator->getErrors();
return; // Stop execution
}
$this->productService->save($productData);
}
}
```
**Best Practices**:
- ✅ Validate all user input
- ✅ Use appropriate rules for each field type
- ✅ Provide clear error messages
- ✅ Use `errorFragment` to update only error section
- ❌ Don't trust client-side validation alone
**Gotchas**:
- Validation errors prevent action execution
- Errors automatically populate `$errors` property
- Validation runs before authorization checks
---
## State Management Attributes
### @Persisted
**Purpose**: Automatically persist component state across requests/sessions.
**Signature**:
```php
#[Persisted(
string $storage = 'session', // Storage type ('session', 'cookie', 'database')
int $ttl = 3600, // TTL in seconds
array $props = [] // Specific props to persist (default: all LiveProps)
)]
```
**Session Persistence**:
```php
final class ShoppingCart extends LiveComponent
{
#[LiveProp]
#[Persisted(storage: 'session')]
public array $items = [];
#[LiveAction]
public function addItem(string $productId, int $quantity): void
{
// Cart persists across page navigations
$this->items[] = [
'product_id' => $productId,
'quantity' => $quantity
];
}
}
```
**Cookie Persistence**:
```php
final class ThemeSwitcher extends LiveComponent
{
#[LiveProp]
#[Persisted(storage: 'cookie', ttl: 31536000)]
public string $theme = 'light';
#[LiveAction]
public function toggleTheme(): void
{
// Theme persists for 1 year
$this->theme = $this->theme === 'light' ? 'dark' : 'light';
}
}
```
**Database Persistence**:
```php
final class DraftEditor extends LiveComponent
{
#[LiveProp]
public string $documentId;
#[LiveProp]
#[Persisted(storage: 'database', ttl: 86400)]
public string $draftContent = '';
#[LiveAction]
public function autosave(string $content): void
{
// Draft persists for 24 hours
$this->draftContent = $content;
}
}
```
**Selective Persistence**:
```php
final class FilterPanel extends LiveComponent
{
#[LiveProp]
public array $filters = [];
#[LiveProp]
public array $results = [];
#[Persisted(storage: 'session', props: ['filters'])]
public function mount(): void
{
// Only 'filters' persisted, not 'results'
}
}
```
**Best Practices**:
- ✅ Use session for temporary state (shopping carts)
- ✅ Use cookies for user preferences (theme, locale)
- ✅ Use database for long-term drafts
- ✅ Set appropriate TTL based on data sensitivity
- ❌ Don't persist large amounts of data
- ❌ Don't persist sensitive data in cookies
**Gotchas**:
- Session storage cleared on logout
- Cookie storage limited to 4KB
- Database storage requires additional queries
---
## Combining Attributes
**Multiple Attributes on Single Action**:
```php
final class PaymentProcessor extends LiveComponent
{
#[LiveAction]
#[RateLimit(requests: 3, window: 3600, key: 'user')]
#[Authorize(permissions: ['payments.process'])]
#[Idempotent(ttl: 86400)]
#[Validated(rules: [
'amount' => ['required', 'numeric', 'min:1'],
'currency' => ['required', 'in:USD,EUR,GBP']
])]
#[Fragment('payment-result')]
public function processPayment(
string $idempotencyKey,
float $amount,
string $currency
): void {
// Secure, rate-limited, idempotent, validated payment processing
$this->paymentService->charge($amount, $currency);
}
}
```
**Execution Order**:
1. **@RateLimit** - Check rate limits first
2. **@Authorize** - Check permissions
3. **@Validated** - Validate parameters
4. **@Idempotent** - Check for duplicate requests
5. **@Cached** - Check cache (if applicable)
6. **Action Execution** - Execute action logic
7. **@Fragment** - Update specific fragments
8. **@Optimistic** - Apply optimistic updates
9. **@Persisted** - Persist state changes
---
## Summary Table
| Attribute | Purpose | Performance Impact | Use Case |
|-----------|---------|-------------------|----------|
| `@LiveProp` | Reactive properties | Low | State synchronization |
| `@LiveAction` | Server actions | Low | User interactions |
| `@Fragment` | Partial updates | **High** (70%+ reduction) | Large components |
| `@RateLimit` | Prevent abuse | Low | Public endpoints |
| `@Authorize` | Access control | Low | Restricted actions |
| `@Idempotent` | Duplicate prevention | Low | Critical operations |
| `@Encrypted` | Data protection | Medium (+30% payload) | Sensitive data |
| `@Optimistic` | Instant UI | **High** (<50ms latency) | Interactive UIs |
| `@Cached` | Response caching | **High** (avoid queries) | Expensive ops |
| `@NoBatch` | Disable batching | Negative (more requests) | File uploads |
| `@Validated` | Input validation | Low | User input |
| `@Persisted` | State persistence | Medium | User preferences |
---
## Debugging Attributes
**Check Attribute Configuration**:
```javascript
// Client-side debugging
const component = LiveComponent.getComponent('component-id');
console.log(component.config.attributes);
// Output:
{
rateLimit: { requests: 60, window: 60 },
fragments: ['user-profile', 'notifications'],
optimistic: { enabled: true, updateProps: ['likes'] },
cached: { ttl: 3600, tags: ['users'] }
}
```
**Server-side Debugging**:
```php
// Get action metadata
$reflection = new \ReflectionMethod(MyComponent::class, 'myAction');
$attributes = $reflection->getAttributes(LiveAction::class);
foreach ($attributes as $attribute) {
var_dump($attribute->getArguments());
}
```
---
## Best Practices Summary
1. **Use `@Fragment`** for components >5KB or with independent sections
2. **Use `@Optimistic`** for non-critical user interactions (likes, votes)
3. **Use `@RateLimit`** on all public endpoints and expensive operations
4. **Use `@Authorize`** for all privileged actions
5. **Use `@Idempotent`** for CREATE/DELETE operations
6. **Use `@Encrypted`** for PII and financial data
7. **Use `@Cached`** for expensive queries (>100ms)
8. **Use `@Validated`** for all user input
9. **Combine attributes** for defense-in-depth security
10. **Test attribute behavior** with integration tests
---
For more examples and advanced patterns, see:
- [Advanced Features Guide](advanced-features.md)
- [Performance Guide](performance-guide.md)
- [Security Guide](security-guide.md)