Diagnostics

LUM4008: Module-scope signal written during SSR

A module-scope signal is written during a server render.

Severity: warning · Category: islands and boundaries (4xxx)

Reported by: planned (not reported by this version). A warning.

#What it means

Modules are loaded once per server process, so a signal declared at module scope is shared by every request. Writing it during SSR leaks one user's state into another user's page.

#Example

tsx
const currentUser = signal<string | null>(null);

export function Header(props: { user: string }) {
  currentUser.set(props.user);
  return <p>{currentUser()}</p>;
}

#How to fix it

Define state that the page or its islands share with defineState(): its factory runs once per request on the server (and once per page in the browser), and islands receive the server's values.

ts
import { defineState, isServer, locals, signal } from "lumiose";

// Seeded from the request on the server; the browser gets the server's value.
export const currentUser = defineState(() => ({
  name: signal<string | null>(isServer ? (locals().user ?? null) : null),
}));

Request state can also live in components, props or context (<Ctx value> and useContext), or in locals on the server. Module-scope signals are fine for browser-only state.