Documentation Index
Fetch the complete documentation index at: https://developers.hubspot.com/docs/llms.txt
Use this file to discover all available pages before exploring further.
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 API | Alternative | Purpose |
|---|
document.querySelector() | UI components | Build interface with React components |
document.getElementById() | React refs | Reference elements within components |
document.body / document.title | Component props and state | Manage UI through React state |
element.innerHTML | JSX and UI components | Render content declaratively |
document.cookie | hubspot.fetch() + backend | Store 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 />);