scaffold module-island
stedefast scaffold module-island
Copies a module's island component source into theme/islands/ so you can edit it freely without forking the module package.
stedefast scaffold module-island <module-id>
Why eject an island?
Module islands ship as compiled JS bundles — you can't edit them directly. Ejecting gives you:
- A readable, editable
.tsxsource file in your theme - Full TypeScript type safety via the module's exported interfaces
- Version drift detection: if a future module version changes an API shape, TypeScript will error at the import site in your ejected island
Supported modules
| Module ID | Package | Island file |
|---|---|---|
comments |
@stedefast/module-comments |
CommentsIsland.tsx |
claps |
@stedefast/module-claps |
ClapsIsland.tsx |
reactions |
@stedefast/module-reactions |
ReactionsIsland.tsx |
analytics |
@stedefast/module-analytics |
AnalyticsIsland.tsx |
newsletter |
@stedefast/module-newsletter |
NewsletterForm.tsx |
contact |
@stedefast/module-contact |
ContactForm.tsx |
search |
@stedefast/module-search |
Search.tsx |
Usage
stedefast scaffold module-island comments
Output:
✔ Created: theme/islands/CommentsIsland.tsx
Next steps:
1. Edit theme/islands/CommentsIsland.tsx
2. In stedefast.config.ts add the path override:
CommentsModule({
islandComponentPath: "./theme/islands/CommentsIsland.tsx",
})
3. Run stedefast build to apply the change.
What the ejected file looks like
The scaffold copies the module's source and rewrites package-relative imports to full package names:
// Before (in published package, relative imports):
import { useComments, postComment } from "../hooks.js";
import type { CommentsIslandProps, SubmitCommentBody } from "../types.js";
// After ejection (in your theme, package imports):
import { useComments, postComment } from "@stedefast/module-comments/hooks";
import type { CommentsIslandProps, SubmitCommentBody } from "@stedefast/module-comments";
The component logic is unchanged — you get the exact same island that ships in the package.
Activating your local copy
Add islandComponentPath to your module config in stedefast.config.ts:
import { CommentsModule } from "@stedefast/module-comments";
export default defineConfig({
modules: [
CommentsModule({
requireApproval: true,
islandComponentPath: "./theme/islands/CommentsIsland.tsx",
}),
],
});
When islandComponentPath is set, Stedefast uses your local file instead of the module's built-in bundle. The asset pipeline compiles it during stedefast build.
Type safety on version upgrades
Ejected islands import their hook functions and prop types from the module package:
import { postComment } from "@stedefast/module-comments/hooks";
import type { SubmitCommentBody } from "@stedefast/module-comments";
If @stedefast/module-comments adds a required field to SubmitCommentBody in a future release, your ejected island will produce a TypeScript error at the build site — you'll know exactly what to update before deploying.
Error cases
| Error | Cause |
|---|---|
Unknown module id |
The ID is not in the supported list |
Package ... is not installed |
Run pnpm add @stedefast/module-<id> first |
File already exists |
The destination .tsx already exists — delete it to re-eject |
Island source directory not found |
The installed package version doesn't include src/island/ — update the package |
Template engine auto-detection
The related scaffold template command also respects your configured template engine:
# If templateEngines: ["liquid", "react"] in stedefast.config.ts
stedefast scaffold template post # → post.liquid (auto-detected)
stedefast scaffold template post --react # → post.tsx (explicit override)
# If templateEngines: ["react"] or not set
stedefast scaffold template post # → post.tsx (default)
stedefast scaffold template post --liquid # → post.liquid (explicit override)
Note: module islands are always React (.tsx) regardless of your site's template engine setting.