Skip to content
HomePagesHomePages template kit

require-island-editor

"Every island must declare whether it is live in the editor canvas."

Venue: eslint — Every island must declare whether it is live in the editor canvas.

A file is an island when its directive prologue says "use client" and it has a default export (see server vs client). Every island must carry:

export const editor = { live: true };

live must be written as true or false literally. A computed value reports.

live is also the whole declaration. Three fields that an older, wider editor carried are gone, and each one reports:

Field What replaced it
shieldMode Nothing to declare — the canvas derives select-first shields from your island’s own markup.
liveSurface The same derivation; the invented surface attributes it needed are gone with it.
props Nothing — an island renders the same props in the canvas as on a published page. To react to what the author selected, read selection from useEditor().

They report rather than being ignored: nothing reads them, but a declaration still reads to a human as a working one, so an island would keep a shield surface or an editor-only prop merge in its source with no way to learn it stopped happening.

Two files are exempt, and neither is a loophole:

  • A module with no "use client" — it is server-rendered code, not an island.
  • A "use client" module with no default export — the platform’s codemod only turns a default-exported client module into an island, so this file never becomes one.

live controls one thing: whether the editor canvas hydrates your island, or leaves it as the static HTML the server rendered. Both answers are legitimate. A gallery lightbox wants live: true so an author can see it behave; a scroll-triggered animation wants live: false so it does not fire while someone is editing copy.

It answers for a top-level island. One island rendered by another hydrates inside its parent’s React tree, so the nested module’s own live is never read — an island that can end up nested gates editor-hostile behavior itself rather than through the flag (see Islands).

The rule exists because the answer used to be expressed by saying nothing. An island with no editor export simply never hydrated in the canvas — which meant “I have not thought about this” and “I want static HTML here” produced identical code. They are not the same statement, and the difference is invisible in review.

live is read once, when your module loads. That is why it must be a literal: a value computed at runtime cannot change the answer, so a non-literal is never a working declaration — only a misleading one.

Declare it. If you are unsure, live: false is the safe answer: it is exactly what your island does today, now written down.

sections/gallery/Lightbox.tsx
"use client";
import { useState } from "react";
export default function Lightbox({ headline }: { headline: string }) {
const [open, setOpen] = useState(false);
return (
<div>
<button onClick={() => setOpen(true)}>Open gallery</button>
{open && <p>{headline}</p>}
</div>
);
}
sections/gallery/Lightbox.tsx
"use client";
import { useState } from "react";
// Authors open the lightbox in the canvas to check it, so hydrate it there.
export const editor = { live: true };
export default function Lightbox({ headline }: { headline: string }) {
const [open, setOpen] = useState(false);
return (
<div>
<button onClick={() => setOpen(true)}>Open gallery</button>
{open && <p>{headline}</p>}
</div>
);
}

Type it if you want the editor to check the shape:

import type { IslandEditor } from "@homepages/template-kit";
export const editor = { live: true } satisfies IslandEditor;
  • Islands — the other half of the contract: the useEditor() hook, and the two patterns a live island uses it for.
  • require-editor-reason — the escape hatch a live island uses instead of a wider editor declaration, and what it must say.
  • Server vs client — the directive, the prologue, and what an island may do.