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
- 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)
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
- 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
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
- Can only set one of `children` or `props.dangerouslySetInner
- Invalid prop '${key}' of type 'function' supplied to '${tag}
- style sheet not found
- Async component is not supported in renderToString
- Invalid JSX tag name: ${tag}
AI-assisted analysis of honojs/hono@e2740d5a1b (2026-08-28).
Data as JSON: /api/errors/8e810aa3beffed2e.
Report an issue: GitHub.