Features
Islands
Islands let you add live components to pages that aren't Nitro pages. The page stays plain HTML, and each component starts on its own: right away, later, or never.
On this page
Not every page needs to be a Nitro page. A marketing page, a blog post or a layout can stay plain Blade
and still contain components. Each component placed this way is an island. The server
renders it with the rest of the page, and the browser starts it on its own. You choose when with
nitro:hydrate. Until an island starts, it is just the server's HTML, and it costs the
browser nothing.
The plain page needs Nitro in its layout as a Nitro page does: @nitroScripts before
</body> (and @nitroHead in the <head> if you want its tags).
Without it, the islands show but never start.
Choosing when an island starts
Add nitro:hydrate with a modifier to the island's tag:
<nitro:comments :post="$post" nitro:hydrate.visible />
<nitro:chat-widget nitro:hydrate.interaction />
<nitro:report-chart nitro:hydrate.idle />
<nitro:mobile-menu nitro:hydrate.media="(max-width: 768px)" />
<nitro:price-table nitro:static />
| Attribute | The island starts |
|---|---|
| (none) | With the page |
nitro:hydrate.idle
|
When the browser is idle |
nitro:hydrate.visible
|
When it comes into view. With
.visible.200px
, when it is that close
|
nitro:hydrate.interaction
|
On the first click, focus, key press or input inside it. Nitro replays the click once the island is live, and passes typed text to
nitro:model
|
nitro:hydrate.media="(query)"
|
When the media query matches |
nitro:static
|
Never. It stays the server's HTML, and no state is sent |
- Before it starts, an island is the server's HTML, and anything typed into it is kept.
- Its slots are rendered again in the browser once it starts.
- Inside a component's view, children always start with their parent, so
nitro:hydratethere is a build error. Islands belong in plain Blade views and layouts.
Placing an island by name
When the component's name is only known at runtime, from a setting or a database row, place it with the
@nitro directive instead of a tag. It takes the name, the props, and optionally its slots'
HTML and when it starts: 'idle', 'visible', 'interaction',
'media:(max-width: 768px)', or 'static' for never:
{{-- The widget each dashboard tile names, from the database --}}
@foreach ($tiles as $tile)
@nitro($tile->component, ['userId' => auth()->id()])
@endforeach
{{-- Starting when it comes into view --}}
@nitro('reports.chart', ['year' => 2026], [], 'visible')
@nitro('posts.table') is the same island as <nitro:posts.table />.
Use the tag when you know the name as you write the view.
Loading an island's code later
Add #[LazyLoad] to the island's class, and the browser downloads its code only when the island starts, not with the page:
use Nitro\Attributes\LazyLoad;
#[LazyLoad('comments')] // Its code is downloaded only when one of its islands starts.
class Comments extends Component
These docs are Nitro pages
Every docs page is a Nitro page, where components start with the page, so the demo has a page of its own. Open it, scroll down, click in the first island, and watch the labels change.