8.0 KiB
Island Directive
Isolated Component Rendering - Render heavy components separately for better performance.
Overview
The #[Island] directive enables isolated rendering of LiveComponents, allowing them to be rendered separately from the main template flow. This is particularly useful for resource-intensive components that can slow down page rendering.
Features
- Isolated Rendering: Components are rendered separately without template wrapper
- Lazy Loading: Optional lazy loading when component enters viewport
- Independent Caching: Islands can have their own caching strategies
- Isolated Events: Islands don't receive parent component events
Basic Usage
Simple Island Component
use App\Framework\LiveComponents\Attributes\LiveComponent;
use App\Framework\LiveComponents\Attributes\Island;
#[LiveComponent('heavy-widget')]
#[Island]
final readonly class HeavyWidgetComponent implements LiveComponentContract
{
// Component implementation
}
Lazy Island Component
#[LiveComponent('metrics-dashboard')]
#[Island(isolated: true, lazy: true, placeholder: 'Loading dashboard...')]
final readonly class MetricsDashboardComponent implements LiveComponentContract
{
// Component implementation
}
Configuration Options
isolated (bool, default: true)
Whether the component should be rendered in isolation. When true, the component is rendered via the /island endpoint without template wrapper.
#[Island(isolated: true)] // Isolated rendering (default)
#[Island(isolated: false)] // Normal rendering (not recommended)
lazy (bool, default: false)
Whether the component should be lazy-loaded when entering the viewport. When true, a placeholder is generated and the component is loaded via IntersectionObserver.
#[Island(lazy: true)] // Lazy load on viewport entry
#[Island(lazy: false)] // Load immediately (default)
placeholder (string|null, default: null)
Custom placeholder text shown while the component is loading (only used when lazy: true).
#[Island(lazy: true, placeholder: 'Loading widget...')]
Template Usage
In Templates
Island components are used the same way as regular LiveComponents:
<x-heavy-widget id="user-123" />
When processed, lazy Islands generate a placeholder:
<div data-island-component="true"
data-live-component-lazy="heavy-widget:user-123"
data-lazy-priority="normal"
data-lazy-threshold="0.1">
<div class="island-placeholder">Loading widget...</div>
</div>
Island vs. Lazy Component
Lazy Component (data-live-component-lazy)
- Loaded when entering viewport
- Part of normal template flow
- Shares state/events with parent components
- Uses
/lazy-loadendpoint
Island Component (data-island-component)
- Rendered in isolation (separate request)
- No template wrapper (no layout/meta)
- Isolated event context (no parent events)
- Optional lazy loading on viewport entry
- Uses
/islandendpoint
Performance Benefits
Isolation
Islands reduce template processing overhead for heavy components by rendering them separately. This means:
- Heavy components don't block main page rendering
- Independent error handling (one island failure doesn't affect others)
- Separate caching strategies per island
Lazy Loading
When combined with lazy loading, Islands provide:
- Reduced initial page load time
- Faster Time to Interactive (TTI)
- Better Core Web Vitals scores
Example Performance Impact
// Without Island: 500ms page load (heavy component blocks rendering)
// With Island: 200ms page load + 300ms island load (non-blocking)
Use Cases
Heavy Widgets
#[LiveComponent('analytics-dashboard')]
#[Island(isolated: true, lazy: true, placeholder: 'Loading analytics...')]
final readonly class AnalyticsDashboardComponent implements LiveComponentContract
{
// Heavy component with complex data processing
}
Third-Party Integrations
#[LiveComponent('external-map')]
#[Island(isolated: true, lazy: true)]
final readonly class ExternalMapComponent implements LiveComponentContract
{
// Component that loads external resources
}
Conditional Components
#[LiveComponent('admin-panel')]
#[Island(isolated: true)]
final readonly class AdminPanelComponent implements LiveComponentContract
{
// Component only visible to admins
// Isolated to prevent template processing for non-admin users
}
Technical Details
Endpoint
Islands are rendered via:
GET /live-component/{id}/island
This endpoint:
- Renders component HTML without template wrapper
- Returns JSON with
html,state,csrf_token - Uses
ComponentRegistry.render()directly (no wrapper)
Frontend Integration
Islands are automatically detected and handled by LazyComponentLoader:
- Lazy islands: Loaded when entering viewport
- Non-lazy islands: Loaded immediately
- Isolated initialization: Islands don't receive parent events
Event Isolation
Islands have isolated event contexts:
- No parent component events
- No shared state with parent components
- Independent SSE channels (if configured)
Best Practices
-
Use Islands for Heavy Components: Only use
#[Island]for components that significantly impact page load time -
Combine with Lazy Loading: Use
lazy: truefor below-the-fold content -
Provide Placeholders: Always provide meaningful placeholder text for better UX
-
Test Isolation: Verify that islands work correctly in isolation
-
Monitor Performance: Track island load times and optimize as needed
Examples
Complete Example
<?php
declare(strict_types=1);
namespace App\Application\LiveComponents\Dashboard;
use App\Framework\LiveComponents\Attributes\LiveComponent;
use App\Framework\LiveComponents\Attributes\Island;
use App\Framework\LiveComponents\Contracts\LiveComponentContract;
use App\Framework\LiveComponents\ValueObjects\ComponentId;
use App\Framework\LiveComponents\ValueObjects\ComponentRenderData;
use App\Framework\LiveComponents\ValueObjects\ComponentState;
#[LiveComponent('metrics-dashboard')]
#[Island(isolated: true, lazy: true, placeholder: 'Loading metrics...')]
final readonly class MetricsDashboardComponent implements LiveComponentContract
{
public function __construct(
public ComponentId $id,
public ComponentState $state
) {
}
public function getRenderData(): ComponentRenderData
{
return new ComponentRenderData(
templatePath: 'livecomponent-metrics-dashboard',
data: [
'componentId' => $this->id->toString(),
'metrics' => $this->loadMetrics(),
]
);
}
private function loadMetrics(): array
{
// Heavy operation - isolated rendering prevents blocking
return [
'users' => 12345,
'orders' => 6789,
'revenue' => 123456.78,
];
}
}
Migration Guide
Converting Regular Component to Island
- Add
#[Island]attribute to component class - Test component rendering (should work identically)
- Optionally enable lazy loading with
lazy: true - Monitor performance improvements
Backward Compatibility
- Components without
#[Island]work exactly as before #[Island]is opt-in (no breaking changes)- Existing lazy components continue to work
Troubleshooting
Island Not Loading
- Check browser console for errors
- Verify component is registered in ComponentRegistry
- Ensure
/islandendpoint is accessible
Placeholder Not Showing
- Verify
lazy: trueis set - Check
placeholderparameter is provided - Inspect generated HTML for
data-island-componentattribute
Events Not Working
- Islands have isolated event contexts
- Use component-specific events, not parent events
- Check SSE channel configuration if using real-time updates