Skip to content

Bug: createDebounce() uses window.setTimeout — crashes in SSR environments #475

@roshinit-a

Description

@roshinit-a

Description

createDebounce() in web-components/src/utils/index.ts calls window.setTimeout directly instead of the globally available setTimeout. This causes a ReferenceError: window is not defined crash when the utility is used in a Node.js SSR environment (e.g. SvelteKit, Nuxt, or any server-rendered integration of these web components).

Current Behaviour

// web-components/src/utils/index.ts
export const createDebounce = (debounceTime: number = 500) => {
  let timeout: number | null = null

  return {
    debounce: (callback: () => void) => {
      if (timeout) {
        clearTimeout(timeout)
      }
      timeout = window.setTimeout(() => {  // ❌ window is not available in SSR
        callback()
        timeout = null
      }, debounceTime)
    },
    ...
  }
}

In Node.js, window is undefined, so any module that imports and calls createDebounce() during SSR will throw:

ReferenceError: window is not defined

Expected Behaviour

The function should use the environment-agnostic setTimeout (without the window. prefix), which works in both browser and Node.js:

- timeout = window.setTimeout(() => {
+ timeout = setTimeout(() => {

setTimeout is part of the global scope in both browser and Node.js environments, so this is a safe, zero-impact change.

Context

This follows the same SSR compatibility pattern as issue #469 / PR #470, where window.matchMedia in dark-mode-listener.ts was guarded for SSR. The createDebounce utility has the same problem but with a simpler fix — no guard needed, just drop the window. prefix.

Proposed Fix

// web-components/src/utils/index.ts

export const createDebounce = (debounceTime: number = 500) => {
  let timeout: number | null = null

  return {
    debounce: (callback: () => void) => {
      if (timeout) {
        clearTimeout(timeout)
      }
-     timeout = window.setTimeout(() => {
+     timeout = setTimeout(() => {
        callback()
        timeout = null
      }, debounceTime)
    },
    clear: () => {
      if (timeout) {
        clearTimeout(timeout)
        timeout = null
      }
    },
  }
}

Happy to open a PR for this if it looks good!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions