honojs/hono · error · Error

getServerSnapshot is required for server side rendering

Error message

getServerSnapshot is required for server side rendering

What it means

This is hono/jsx's useSyncExternalStore. When there is no build-data frame on buildDataStack (i.e. during stringification/SSR rather than interactive client rendering), the hook cannot call getSnapshot to read the live store, so it requires getServerSnapshot to produce an initial server value; without it, it throws.

Source

Thrown at src/jsx/hooks/index.ts:410

): void => {
  useEffect(() => {
    ref.current = createHandle()
    return () => {
      ref.current = null
    }
  }, deps)
}

export const useSyncExternalStore = <T>(
  subscribe: (callback: () => void) => () => void,
  getSnapshot: () => T,
  getServerSnapshot?: () => T
): T => {
  const buildData = buildDataStack.at(-1) as [Context, unknown]
  if (!buildData) {
    // now a stringify process, maybe in server side
    if (!getServerSnapshot) {
      throw new Error('getServerSnapshot is required for server side rendering')
    }
    return getServerSnapshot()
  }

  const snapshot =
    buildData[0][4] && getServerSnapshot ? (getServerSnapshot as () => T)() : getSnapshot()
  const [, setVersion] = useState(0)
  const latestSnapshot = useRef<[T, () => T]>([snapshot, getSnapshot])
  latestSnapshot.current = [snapshot, getSnapshot]
  const unsubscribeRef = useRef<() => void>(null)

  // Swap subscriptions at effect flush: a returned cleanup would run synchronously
  // during render when `subscribe` changes, leaving a window with no subscription.
  useEffect(() => {
    const update = () => setVersion((version) => version + 1)
    unsubscribeRef.current?.()
    unsubscribeRef.current = subscribe(update)
    const [snapshot, getSnapshot] = latestSnapshot.current!

View on GitHub (pinned to e2740d5a1b)

Solutions

  1. Pass getServerSnapshot as the third argument: useSyncExternalStore(subscribe, getSnapshot, () => serverValue)
  2. Reuse getSnapshot as getServerSnapshot if the store is readable on the server (store state is plain JSON, no browser APIs)
  3. Skip SSR for that subtree and render it client-side only (or gate the hook behind a client check)

Example fix

// before
const state = useSyncExternalStore(subscribe, getSnapshot)
// after
const state = useSyncExternalStore(subscribe, getSnapshot, () => initialState)
Defensive patterns

Strategy: validation

Validate before calling

useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot ?? (() => initialState))

Type guard

null

Try / catch

null

Prevention

When it happens

Trigger: Calling useSyncExternalStore(subscribe, getSnapshot) — omitting the third getServerSnapshot argument — in a component that is server-side rendered or stringified by hono/jsx; also hitting the hydration/stringify path where buildDataStack is empty.

Common situations: Porting React code that happened to omit getServerSnapshot (React warns; Hono throws); adding a store-backed hook to a component newly included in SSR; version upgrades that changed hono/jsx's SSR handling of hooks.

Related errors


AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28). Data as JSON: /api/errors/8e810aa3beffed2e. Report an issue: GitHub.