Island import 규칙. 반드시 @mandujs/core/client에서 import. "use client", .island.tsx, useState, hydration 작업 시 자동 호출.
Scanned 9/9/2026
Install to Claude Code
npx -y skills add konamgil/mandu --skill mandu-hydration --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Mandu Hydration?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/konamgil-mandu-hydration-mandu)More formats (shields.io, HTML) on the badges page.
---
name: mandu-hydration
description: |
Island import 규칙. 반드시 @mandujs/core/client에서 import.
"use client", .island.tsx, useState, hydration 작업 시 자동 호출.
---
# Mandu Island Hydration
Island Hydration은 페이지의 일부분만 클라이언트에서 인터랙티브하게 만드는 기술입니다.
## Import Rule (CRITICAL)
Client islands MUST import from `@mandujs/core/client`, NOT `@mandujs/core`.
The main module includes server-side dependencies that break client bundles.
```typescript
// CORRECT
import { island } from "@mandujs/core/client";
import { useServerData, useHydrated, useIslandEvent } from "@mandujs/core/client";
// WRONG - will cause build errors or bloated bundles
import { island } from "@mandujs/core";
```
## Island File Conventions
| File | Purpose |
|------|---------|
| `*.island.tsx` | Island component (auto-detected for hydration) |
| `*.client.tsx` | Client-only logic file |
| `*.slot.ts` / `*.slot.tsx` | Server-side data loader |
All island files MUST have `"use client"` directive at the very top.
## island() API
`island()` takes a single definition object. Do not call
`island("visible", Component)`; that is not a supported runtime API.
### Simple component wrapper
```tsx
// app/counter.island.tsx
"use client";
import { wrapComponent } from "@mandujs/core/client";
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return <button onClick={() => setCount(c => c + 1)}>{count}</button>;
}
export default wrapComponent(Counter);
```
### Client Island with Setup (advanced)
```tsx
// app/chat.island.tsx
"use client";
import { island } from "@mandujs/core/client";
import { useState } from "react";
export default island<ServerData>({
setup(data) {
const [messages, setMessages] = useState(data.initialMessages);
return { messages, setMessages };
},
render({ messages, setMessages }) {
return <div>{messages.map(m => <p key={m.id}>{m.text}</p>)}</div>;
}
});
```
Islands are page-level client bundles. They are discovered from route/client
modules and rendered by the framework wrapper, not embedded as inline JSX. If
you need an interactive region inside a server page, use `partial()` and render
its `.Render` component.
```tsx
"use client";
import { partial } from "@mandujs/core/client";
const CounterPartial = partial({
component: Counter,
priority: "visible",
});
export function Header() {
return <CounterPartial.Render initialCount={0} />;
}
```
## Hydration Priorities
| Priority | When loaded | Use case |
|----------|------------|----------|
| `"immediate"` | Page load | Critical interactions (nav, auth) |
| `"visible"` | In viewport | Default - most components |
| `"idle"` | Browser idle | Below-fold content |
| `"interaction"` | User interacts | Heavy widgets (editor, map) |
```tsx
// app/page.tsx or manifest route config
export const hydration = {
strategy: "island",
priority: "visible",
preload: false,
};
// Inline client regions use partial({ priority }).
partial({ component: CommentSection, priority: "visible" });
```
## Client Hooks
```typescript
import { useServerData, useHydrated, useIslandEvent } from "@mandujs/core/client";
// Access data from server slot
const data = useServerData<UserData>("user", defaultValue);
// Check if component has hydrated (SSR-safe code branching)
const isHydrated = useHydrated();
// Cross-island communication
const { emit, on } = useIslandEvent();
emit("cart:updated", { items: newItems });
on("cart:updated", (data) => setCartItems(data.items));
```
## Server Data Flow
```typescript
// spec/slots/user-profile.slot.ts (server)
import { Mandu } from "@mandujs/core";
export default Mandu.filling()
.get(async (ctx) => {
const user = await db.getUser(ctx.params.id);
return ctx.ok({ user }); // Data available to Islands
});
// app/user-profile.island.tsx (client)
"use client";
import { useServerData } from "@mandujs/core/client";
export default function UserProfile() {
const { user } = useServerData("user-profile");
return <div>{user.name}</div>;
}
```
## Common Mistakes
- Importing from `@mandujs/core` instead of `@mandujs/core/client` in Islands
- Forgetting `"use client"` directive in island files
- Calling `island("visible", Component)` instead of `island({ setup, render })` or `wrapComponent(Component)`
- Rendering a page-level island as `<MyIsland />`; use `partial()` for inline client regions
- Using `useState`/`useEffect` in server components (non-island files)
- Setting all Islands to `"immediate"` priority (defeats partial hydration)
- Putting heavy server imports in `.island.tsx` files (increases bundle size)
## See also
- `mandu-mcp-verify` — island 편집 후 `mandu_doctor` (import/location 진단) drill-down
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!