Table of Contents

Browser runtime

NiteFx adds a little JavaScript to your pages: the island loader in development and production, and the dev client and Fast Refresh runtime in development only.

Events

nite:server-update

Dispatched on window in development when C# hot reload changes the running server.

addEventListener("nite:server-update", (event) => {
  const { types } = (event as CustomEvent<{ types: string[] }>).detail;
  console.log("server changed:", types);   // ["MyApp.TodoStore", ...]
  reload();
});
detail property Type Meaning
types string[] Full names of the .NET types the update changed (may be empty).

It never fires in production. A small helper that fits useEffect:

export function onServerUpdate(callback: () => void) {
  addEventListener("nite:server-update", callback);
  return () => removeEventListener("nite:server-update", callback);
}

The dev client

/_nite/client.js, injected in development. It:

  • connects to the dev server's WebSocket (/_nite/ws) and applies messages: hot updates, stylesheet swaps, reloads and errors;
  • shows the error overlay (a <nite-error-overlay> element with its own shadow DOM) and clears it when the failing file is fixed;
  • reconnects when the connection drops, and if the server restarted, reloads the page once the server answers again;
  • dispatches nite:server-update.

Updates are logged to the console at the debug level ([nite] hot updated src/App.tsx).

The Fast Refresh runtime

/_nite/refresh.js, injected before any React code runs when Fast Refresh is enabled. It sets up react-refresh's runtime, which modules compiled by the dev server register their components with. Nothing in your code calls it.

The island loader

Loaded by <nite-head> on pages with islands. It finds elements with a data-island attribute, loads each one's module when its strategy says so, and mounts the component through the renderer.

data-client Loads
load (or none) Immediately
idle On requestIdleCallback (2 s timeout), or after 200 ms where unsupported
visible When the element is within 200 px of the viewport (IntersectionObserver)
media When matchMedia(data-media) matches, now or later

A MutationObserver on the document mounts islands added later and unmounts islands that are removed, calling the cleanup function the renderer returned. An island that fails to load logs [nite] island <name> failed: with the error and does not affect other islands.

The renderer contract

export function mount(
  element: HTMLElement,
  component: unknown,
  props: object,
): void | (() => void) | Promise<void | (() => void)>;

The default renderer uses React's createRoot. See Custom renderers.

Global names

The runtime defines no globals in production. In development, window.__NITE_REFRESH__ holds the Fast Refresh runtime when it is enabled, and react-refresh installs React's DevTools hook (__REACT_DEVTOOLS_GLOBAL_HOOK__) if no DevTools extension already has.