{"record":{"id":"8e810aa3beffed2e","repo":"honojs/hono","slug":"getserversnapshot-is-required-for-server-side-rend","errorCode":null,"errorMessage":"getServerSnapshot is required for server side rendering","messagePattern":"getServerSnapshot is required for server side rendering","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/jsx/hooks/index.ts","lineNumber":410,"sourceCode":"): void => {\n  useEffect(() => {\n    ref.current = createHandle()\n    return () => {\n      ref.current = null\n    }\n  }, deps)\n}\n\nexport const useSyncExternalStore = <T>(\n  subscribe: (callback: () => void) => () => void,\n  getSnapshot: () => T,\n  getServerSnapshot?: () => T\n): T => {\n  const buildData = buildDataStack.at(-1) as [Context, unknown]\n  if (!buildData) {\n    // now a stringify process, maybe in server side\n    if (!getServerSnapshot) {\n      throw new Error('getServerSnapshot is required for server side rendering')\n    }\n    return getServerSnapshot()\n  }\n\n  const snapshot =\n    buildData[0][4] && getServerSnapshot ? (getServerSnapshot as () => T)() : getSnapshot()\n  const [, setVersion] = useState(0)\n  const latestSnapshot = useRef<[T, () => T]>([snapshot, getSnapshot])\n  latestSnapshot.current = [snapshot, getSnapshot]\n  const unsubscribeRef = useRef<() => void>(null)\n\n  // Swap subscriptions at effect flush: a returned cleanup would run synchronously\n  // during render when `subscribe` changes, leaving a window with no subscription.\n  useEffect(() => {\n    const update = () => setVersion((version) => version + 1)\n    unsubscribeRef.current?.()\n    unsubscribeRef.current = subscribe(update)\n    const [snapshot, getSnapshot] = latestSnapshot.current!","sourceCodeStart":392,"sourceCodeEnd":428,"githubUrl":"https://github.com/honojs/hono/blob/e2740d5a1bd0b4254e517e3af8b60789284bc7bd/src/jsx/hooks/index.ts#L392-L428","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass getServerSnapshot as the third argument: useSyncExternalStore(subscribe, getSnapshot, () => serverValue)","Reuse getSnapshot as getServerSnapshot if the store is readable on the server (store state is plain JSON, no browser APIs)","Skip SSR for that subtree and render it client-side only (or gate the hook behind a client check)"],"exampleFix":"// before\nconst state = useSyncExternalStore(subscribe, getSnapshot)\n// after\nconst state = useSyncExternalStore(subscribe, getSnapshot, () => initialState)","handlingStrategy":"validation","validationCode":"useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot ?? (() => initialState))","typeGuard":"null","tryCatchPattern":"null","preventionTips":["Always pass the third getServerSnapshot argument when a component may be SSR'd","Keep server snapshots serializable and browser-API-free","Centralize store hooks in a wrapper that defaults getServerSnapshot"],"tags":["hono","jsx","ssr","hooks","usesyncexternalstore"],"backgroundTag":"missing-getserversnapshot","analyzedSha":"e2740d5a1bd0b4254e517e3af8b60789284bc7bd","analyzedAt":"2026-08-28T10:18:08.750Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}