Skip to main content
The no-dom-access rule prevents usage of the document object and DOM APIs in UI extensions.

Rule details

UI extensions run in a sandboxed web worker environment where the DOM is intentionally unavailable. This architectural constraint ensures security and stability by preventing extensions from accessing sensitive host application data, modifying the main HubSpot interface, or interfering with other extensions. The document object and DOM APIs don’t exist at runtime. Attempting to use them will cause runtime errors. Instead, build your UI entirely with React components and hooks from @hubspot/ui-extensions.

DOM alternatives

Use the following UI extension alternatives instead of DOM APIs:
DOM APIAlternativePurpose
document.querySelector()UI componentsBuild interface with React components
document.getElementById()React refsReference elements within components
document.body / document.titleComponent props and stateManage UI through React state
element.innerHTMLJSX and UI componentsRender content declaratively
document.cookiehubspot.fetch() + backendStore data via backend APIs
See the next section for an example of each alternative.

Examples

Querying elements

Instead of using document.querySelector() or document.getElementById():
// Incorrect
const element = document.querySelector('.my-element');
const body = document.body;
const title = window.document.title;
const cookie = window['document'].cookie;
Build your UI with UI components:
import { Text, Button, Flex, Heading } from '@hubspot/ui-extensions';
import { hubspot } from '@hubspot/ui-extensions';
import { useState } from 'react';

const Extension = () => {
  const [count, setCount] = useState(0);

  return (
    <Flex direction="column" gap="medium">
      <Heading>Counter Example</Heading>
      <Text>Count: {count}</Text>
      <Button onClick={() => setCount(count + 1)}>
        Increment
      </Button>
    </Flex>
  );
};

hubspot.extend(() => <Extension />);

Element references

Instead of using document.getElementById() in effects:
// Incorrect
import { useEffect } from 'react';

function Component() {
  useEffect(() => {
    const element = document.getElementById('my-id');
    element.focus();
  }, []);

  return <Text>Component</Text>;
}
Use React refs and component props:
import { Input, Flex } from '@hubspot/ui-extensions';
import { hubspot } from '@hubspot/ui-extensions';
import { useState, useRef, useEffect } from 'react';

const Extension = () => {
  const [name, setName] = useState('');
  const [email, setEmail] = useState('');
  const nameInputRef = useRef(null);

  useEffect(() => {
    // Focus the input using ref
    if (nameInputRef.current) {
      nameInputRef.current.focus();
    }
  }, []);

  return (
    <Flex direction="column" gap="small">
      <Input
        ref={nameInputRef}
        label="Name"
        value={name}
        onChange={(value) => setName(value)}
      />
      <Input
        label="Email"
        value={email}
        onChange={(value) => setEmail(value)}
      />
    </Flex>
  );
};

hubspot.extend(() => <Extension />);

Data fetching

Instead of fetching data and manipulating the DOM:
// Incorrect
useEffect(() => {
  fetch('/api/user')
    .then(res => res.json())
    .then(data => {
      document.getElementById('username').textContent = data.name;
    });
}, []);
Use CRM data fetching hooks and React state:
import { Text, LoadingSpinner, Flex } from '@hubspot/ui-extensions';
import { hubspot, logger } from '@hubspot/ui-extensions';
import { useCrmProperties } from '@hubspot/ui-extensions/crm';
import { useState, useEffect } from 'react';

const Extension = () => {
  const { properties, isLoading, error } = useCrmProperties([
    'firstname',
    'lastname',
    'email',
  ]);

  const [displayName, setDisplayName] = useState('');

  useEffect(() => {
    if (properties.firstname && properties.lastname) {
      const fullName = `${properties.firstname} ${properties.lastname}`;
      setDisplayName(fullName);
      logger.info(`Loaded contact: ${fullName}`);
    }
  }, [properties]);

  if (isLoading) return <LoadingSpinner />;
  if (error) return <Text>Error loading data</Text>;

  return (
    <Flex direction="column" gap="small">
      <Text format={{ fontWeight: 'bold' }}>{displayName}</Text>
      <Text>{properties.email}</Text>
    </Flex>
  );
};

hubspot.extend(() => <Extension />);
Last modified on February 19, 2026