Islands Architecture
Islands allow you to embed interactive Luna (MoonBit) components within static pages. The page is rendered as static HTML, and specific interactive regions ("islands") are hydrated on the client.
Overview
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Static HTML (SSG) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Island (Interactive) โ โ
โ โ - Luna/MoonBit component โ โ
โ โ - Hydrated on client โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ More static content... โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Configuration
1. sol.config.json
Configure the islands directory:
{
"islands": {
"dir": "docs/public/islands",
"basePath": "/islands/"
}
}
2. Build the Component
Create a MoonBit component with a hydrate export:
// src/examples/wiki/main.mbt
pub fn hydrate(el : @dom.Element, _state : @js.Any) -> Unit {
// Initialize your component
let container = el |> @luna_dom.DomElement::from_dom
// ... render logic
}
Package configuration (moon.pkg.json):
{
"is-main": true,
"supported-targets": ["js"],
"import": [
"mizchi/luna/signal",
{ "path": "mizchi/luna/dom/element", "alias": "dom" },
{ "path": "mizchi/js/browser/dom", "alias": "js_dom" },
{ "path": "mizchi/js/core", "alias": "js" }
],
"link": {
"js": {
"format": "esm",
"exports": ["hydrate"]
}
}
}
3. Copy to Islands Directory
After building, copy the compiled JS to the islands directory:
moon build --target js
cp target/js/release/build/examples/wiki/wiki.js docs/public/islands/
4. Use in Markdown
Reference the island in your markdown:
---
title: Wiki
islands:
- wiki
---
# Wiki Demo
<Island name="wiki" trigger="load" />
Island Directive Syntax
<Island name="component-name" trigger="load" />
Attributes
| Attribute | Description |
|---|---|
name | Component name (matches filename without .js) |
trigger | Hydration trigger (see below) |
Trigger Types
| Trigger | Description |
|---|---|
load | Hydrate immediately on page load |
idle | Hydrate when browser is idle |
visible | Hydrate when element enters viewport |
Client-Side Routing with BrowserRouter
Islands can implement full client-side routing using Luna's BrowserRouter:
fn create_routes() -> Array[@routes.Routes] {
[
Page(path="", component="home", title="Home", meta=[]),
Page(path="/:slug", component="page", title="Page", meta=[]),
]
}
pub fn hydrate(el : @dom.Element, _state : @js.Any) -> Unit {
let base = "/wiki"
let routes = create_routes()
let router = @router.BrowserRouter::new(routes, base~)
let container = el |> @luna_dom.DomElement::from_dom
render_app(router, container)
}
This enables:
Client-side navigation without page reload
Dynamic URL parameters (
:slug)Browser history (back/forward) support
Generated HTML
The Island directive generates:
<!--luna:island:wiki url=/islands/wiki.js trigger=load-->
<div luna:id="wiki"
luna:url="/islands/wiki.js"
luna:state="{}"
luna:client-trigger="load">
</div>
<!--/luna:island:wiki-->
The Luna loader automatically discovers and hydrates these elements.
Example: Wiki with Dynamic Routing
See examples/sol_docs/docs/wiki/ for a complete example:
Static entry:
/wiki/- Single HTML page with island placeholderClient routing: BrowserRouter handles
/wiki/:slugpatternsNo server needed: Fully static, works with any file server
/wiki/ โ Wiki Home (index page)
/wiki/getting-started โ Handled by BrowserRouter
/wiki/configuration โ Handled by BrowserRouter
Islands vs Dynamic Routes
| Use Case | Solution |
|---|---|
| Blog posts (SEO important) | Dynamic Routes (_slug_) |
| Interactive documentation | Islands |
| SPA within static site | Islands + BrowserRouter |
| Static content pages | Regular Markdown |