ワンクリックで
mandu-hydration
Island import 규칙. 반드시 @mandujs/core/client에서 import. "use client", .island.tsx, useState, hydration 작업 시 자동 호출.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Island import 규칙. 반드시 @mandujs/core/client에서 import. "use client", .island.tsx, useState, hydration 작업 시 자동 호출.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Canonical Mandu agent workflow. Use first in Mandu projects before direct source edits so Codex, Claude Code, Gemini CLI, and other agents follow the same context -> plan -> apply -> verify -> repair loop.
React composition patterns for Mandu applications. Use when designing Island components, managing shared state, or building reusable component APIs. Triggers on compound components, context providers, boolean props, or component architecture tasks.
Production deployment patterns for Mandu applications
File-system based routing for Mandu. Use when creating pages, API routes, layouts, or dynamic routes. Triggers on tasks involving app/ folder, page.tsx, route.ts, layout.tsx, [id], [...slug], or URL patterns.
Architecture guard system for Mandu. Use when checking layer dependencies, enforcing architecture rules, or validating file locations. Triggers on tasks involving architecture, layers, dependencies, or guard commands.
Island Hydration pattern for Mandu. Use when creating interactive components, client-side state, or partial hydration. Triggers on tasks involving "use client", client.tsx, useState, useEffect, Island, or hydration.
| name | mandu-hydration |
| description | Island import 규칙. 반드시 @mandujs/core/client에서 import. "use client", .island.tsx, useState, hydration 작업 시 자동 호출. |
Island Hydration은 페이지의 일부분만 클라이언트에서 인터랙티브하게 만드는 기술입니다.
Client islands MUST import from @mandujs/core/client, NOT @mandujs/core.
The main module includes server-side dependencies that break client bundles.
// 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";
| 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() takes a single definition object. Do not call
island("visible", Component); that is not a supported runtime API.
// 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);
// 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.
"use client";
import { partial } from "@mandujs/core/client";
const CounterPartial = partial({
component: Counter,
priority: "visible",
});
export function Header() {
return <CounterPartial.Render initialCount={0} />;
}
| 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) |
// 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" });
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));
// 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>;
}
@mandujs/core instead of @mandujs/core/client in Islands"use client" directive in island filesisland("visible", Component) instead of island({ setup, render }) or wrapComponent(Component)<MyIsland />; use partial() for inline client regionsuseState/useEffect in server components (non-island files)"immediate" priority (defeats partial hydration).island.tsx files (increases bundle size)mandu-mcp-verify — island 편집 후 mandu_doctor (import/location 진단) drill-down