Skip to content

SSR ClientOnly

This guide explains when to use ClientOnly in Zova SSR within the Cabloy monorepo.

When to use ClientOnly

Some components only make sense on the client side.

When that happens, wrap them in ClientOnly so SSR does not try to render behavior that depends on the browser-only environment.

Representative pattern

typescript
import { ClientOnly } from 'zova';

@Render()
export class RenderTabs {
  render() {
    return (
      <ClientOnly>
        <div role="tablist" class="tabs tabs-lifted">
          {domTabs}
        </div>
      </ClientOnly>
    );
  }
}

Why this matters

This is one of the simplest but most important SSR boundary tools.

It makes the server/client split explicit and keeps browser-only behavior from leaking into the server render path.

Routed dialogs are client-only

this.$appModal.routedDialog(...) is a runtime-enforced client-only API in the current Cabloy Basic implementation. Open it from a browser interaction in the normal case, and do not call it from a server-rendered execution path merely because the target page or its host supports SSR.

If lifecycle-driven opening is genuinely required, defer it to an explicit post-hydration client boundary. ClientOnly protects browser-only rendering; it does not make a server-side routed-dialog call valid. See Routed Dialog Guide for the local-router, result, and hydration boundary.

Implementation checks for client-only SSR boundaries

When adding or editing SSR-sensitive UI, ask whether the component depends on client-only behavior such as browser APIs, client-only rendering expectations, or interactions that should not appear in the server render.

If yes, ClientOnly may be the right boundary.

Released under the MIT License.