Stedefast

scaffold module-island

3 min read

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 .tsx source 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.