diff --git a/src/no-ssr/index.ts b/src/no-ssr/index.ts new file mode 100644 index 00000000..b4b68cd7 --- /dev/null +++ b/src/no-ssr/index.ts @@ -0,0 +1,11 @@ +export const noSSR = (extraMessage?: string) => { + const error = new Error(extraMessage); + + // Next.js marks errors with `NEXT_DYNAMIC_NO_SSR_CODE` digest as recoverable: + // https://github.com/vercel/next.js/blob/ded28edeae16f8f8b4b9b117a83b5232e3623029/packages/next/src/client/on-recoverable-error.ts#L3 + (error as any).digest = 'NEXT_DYNAMIC_NO_SSR_CODE'; + + (error as any).recoverableError = 'NO_SSR'; + + throw error; +}; diff --git a/src/use-local-storage/index.ts b/src/use-local-storage/index.ts index fd66a45c..6ad0b74c 100644 --- a/src/use-local-storage/index.ts +++ b/src/use-local-storage/index.ts @@ -1,6 +1,7 @@ import { useSyncExternalStore, useCallback, useEffect } from 'react'; import { noop } from '../noop'; import { useIsomorphicLayoutEffect } from '../use-isomorphic-layout-effect'; +import { noSSR } from '../no-ssr'; function dispatchStorageEvent(key: string, newValue: string | null) { if (typeof window !== 'undefined') { @@ -54,6 +55,8 @@ const subscribeToLocalStorage = (callback: () => void) => { return noop; }; +const getServerSnapshotWithoutServerValue = () => noSSR('foxact: useLocalStorage without `serverValue` will only be used at client side.'); + // This type utility is only used for workaround https://github.com/microsoft/TypeScript/issues/37663 // eslint-disable-next-line @typescript-eslint/ban-types -- workaround TypeScript bug const isFunction = (x: unknown): x is Function => typeof x === 'function'; @@ -65,7 +68,7 @@ export const useLocalStorage = (key: string, serverVa // If the serverValue is not provided, we don't pass it to useSES, which will cause useSES to opt-in client-side rendering const getServerSnapshot = typeof serverValue !== 'undefined' ? () => JSON.stringify(serverValue) - : undefined; + : getServerSnapshotWithoutServerValue; const store = useSyncExternalStore( subscribeToLocalStorage,