dotnet/AspNetCore.Docs · error · Error

Unable to bind DOM element: ${id}

Error message

Unable to bind DOM element: ${id}

What it means

The SignalR chat sample's `dom<T>` helper calls `document.getElementById(id)` and throws if the result is null. It is a hard fail to surface missing DOM bindings at startup rather than letting undefined element access cascade.

Source

Thrown at aspnetcore/signalr/authn-and-authz/sample/wwwroot/js/chat.ts:5

// DOM Binding
function dom<T extends HTMLElement>(id: string): T {
    const element = document.getElementById(id);
    if (!element) {
        throw new Error(`Unable to bind DOM element: ${id}`);
    }
    return element as T;
}

function showIf(condition: any, ifTrue: HTMLElement, ifFalse?: HTMLElement) {
    ifTrue.style.display = condition ? "inherit" : "none";

    if (ifFalse) {
        ifFalse.style.display = condition ? "none" : "inherit";
    }
}

const chatDiv = dom<HTMLDivElement>("chatDiv");
const errorDiv = dom<HTMLDivElement>("errorDiv");
const logoutButton = dom<HTMLButtonElement>("logoutButton");
const connectingDiv = dom<HTMLDivElement>("connectingDiv");
const connectedDiv = dom<HTMLDivElement>("connectedDiv");
const messageForm = dom<HTMLFormElement>("messageForm");

View on GitHub (pinned to c67a80103a)

Solutions

  1. Ensure the script runs after the element exists — move the `<script>` to the end of `<body>` or wrap startup in `DOMContentLoaded`.
  2. Verify the id in the markup exactly matches the string passed to `dom()`.
  3. For conditionally-rendered elements, call `dom()` after they are inserted rather than at module load.

Example fix

// before (runs before DOM ready)
const btn = dom<HTMLButtonElement>('sendButton');

// after
document.addEventListener('DOMContentLoaded', () => {
  const btn = dom<HTMLButtonElement>('sendButton');
});
Defensive patterns

Strategy: validation

Validate before calling

function safeDom<T extends HTMLElement>(id: string): T | null {
  return document.getElementById(id) as T | null;
}

function requireDom<T extends HTMLElement>(id: string): T {
  const el = safeDom<T>(id);
  if (!el) throw new Error(`Unable to bind DOM element: ${id}`);
  return el;
}

Type guard

function isElementBound(id: string): boolean {
  return document.getElementById(id) !== null;
}

Try / catch

let btn: HTMLButtonElement;
try {
  btn = dom<HTMLButtonElement>('sendButton');
} catch (e) {
  if (/Unable to bind/.test(e.message)) {
    console.error('Missing element — check markup and script timing:', e.message);
    return;
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling `dom<HTMLElement>('someId')` when no element with `id="someId"` exists in the page at call time.

Common situations: Script runs before the DOM is parsed (script in `<head>` without defer); element id renamed in markup but not in TS; conditional UI where the element is rendered later; typo in the id string; template not yet rendered by a framework.

Related errors


AI-assisted analysis of dotnet/AspNetCore.Docs@c67a80103a (2026-08-13). Data as JSON: /api/errors/bac2b874f660a4f0. Report an issue: GitHub.