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.
Hooks are used to simplify accessing context, performing actions, and fetching CRM data within UI extensions. The hooks provided by the UI extensions SDK are optimized to prevent unnecessary re-renders and automatically clean up resources when components unmount. You can pass inline arrays and objects to the hooks directly, as memoization is not required.
Universal hooks
Universal hooksSupported in all extension points: crm.record.tab, crm.record.sidebar, crm.preview, helpdesk.sidebar, settings, home| Hook | Description |
|---|
useExtensionApi | Access both context and actions from a single hook. |
useExtensionContext | Access contextual information about the extension environment. |
useExtensionActions | Access actions that can be performed within HubSpot. |
useCrmSearch | Search CRM records by query or structured filters. |
import {
useExtensionApi,
// OR
useExtensionContext,
useExtensionActions,
useCrmSearch
} from "@hubspot/ui-extensions";
useExtensionApi
The useExtensionApi hook provides access to available actions and contextual information from a single hook, for extension components that need access to both actions and context.
Otherwise, it’s best practice to use the more specific useExtensionActions or useExtensionContext, depending on your use case.
The following example uses the useExtensionApi hook to display an alert (action) containing the user’s first name (context) when a button is clicked.
import { Button, hubspot, useExtensionApi } from '@hubspot/ui-extensions';
hubspot.extend(() => <MyExtension />);
function MyExtension() {
const { actions, context } = useExtensionApi();
return (
<Button onClick={() => actions.addAlert({ message: `Hello ${context.user.firstName}!` })}>
Click me
</Button>
);
}
import { Button, hubspot, useExtensionApi } from '@hubspot/ui-extensions';
hubspot.extend<'crm.record.tab'>(() => <MyExtension />);
function MyExtension() {
const { actions, context } = useExtensionApi<'crm.record.tab'>();
return (
<Button onClick={() => actions.addAlert({ message: `Hello ${context.user.firstName}!` })}>
Click me
</Button>
);
}
useExtensionContext
The useExtensionContext hook provides access to contextual information about the current extension environment, including location and other relevant data.
For a complete list of available context properties, see the context reference documentation.
The following example accesses the current extension location and renders it in a Text component:
import { Text, hubspot, useExtensionContext } from '@hubspot/ui-extensions';
hubspot.extend(() => <MyExtension />);
function MyExtension() {
const context = useExtensionContext();
return (
<Text>Current location: {context.location}</Text>
);
}
import { Text, hubspot, useExtensionContext } from '@hubspot/ui-extensions';
hubspot.extend<'crm.record.tab'>(() => <MyExtension />);
function MyExtension() {
const context = useExtensionContext<'crm.record.tab'>();
return (
<Text>Current location: {context.location}</Text>
);
}
useExtensionActions
The useExtensionActions hook provides access to various actions that can be performed within the HubSpot interface. It’s a generic hook that can be typed with specific extension point locations for better TypeScript support.
For a complete list of available actions, see the actions reference documentation. The actions available depend on the extension point location.
The following example displays an alert when a button is clicked:
import { Button, hubspot, useExtensionActions } from '@hubspot/ui-extensions';
hubspot.extend(() => <MyExtension />);
function MyExtension() {
const { addAlert } = useExtensionActions();
return (
<Button onClick={() => addAlert({ message: "Action completed!" })}>
Click me
</Button>
);
}
import { Button, hubspot, useExtensionActions } from '@hubspot/ui-extensions';
hubspot.extend<'crm.record.tab'>(() => <MyExtension />);
function MyExtension() {
const { addAlert } = useExtensionActions<'crm.record.tab'>();
return (
<Button onClick={() => addAlert({ message: "Action completed!" })}>
Click me
</Button>
);
}
useCrmSearch
The useCrmSearch hook searches CRM records of a given object type. The search is built using any combination of text query and structured filters, and returns record IDs, formatted properties, and optional associations.
Call
Response
Example usage
Live search
Filter by association
useCrmSearch(
{
objectType: "contact",
properties: ["firstname", "lastname", "email"],
query: "john",
filterGroups: [
{
filters: [
{ propertyName: "createdate", operator: "GT", value: "1704067200000" }
]
}
],
sorts: [{ propertyName: "createdate", direction: "DESCENDING" }],
pageLength: 20,
},
{
propertiesToFormat: "all",
formattingOptions: {
date: {
format: "MM-DD-YYYY",
relative: false,
},
dateTime: {
format: "MM-DD-YYYY hh:mm",
relative: false,
},
currency: {
addSymbol: true,
},
},
}
);
Configures the search request with:
objectType (required): the object type to search. Accepts type IDs ("0-1"), names ("contact", "deal"), or custom object names with a p_ prefix ("p_pets").
properties: an optional array of properties to return for each result.
query: an optional free-text search query matched against the object’s default searchable properties.
filterGroups: an optional array of filter groups. Groups in the array are OR’d together; filters within a single group are AND’d. Each filter includes a propertyName, an operator (EQ, NEQ, LT, LTE, GT, GTE, BETWEEN, IN, NOT_IN, HAS_PROPERTY, NOT_HAS_PROPERTY, CONTAINS_TOKEN, NOT_CONTAINS_TOKEN), and a value, values, or highValue field depending on the operator. See search the CRM for more details.
sorts: an optional array of sort configurations. Each item requires a propertyName and a direction ("ASCENDING" or "DESCENDING").
pageLength: an optional number of results per page. Default: 10. Max: 200.
Either 'all' or an array of property names to format.
Contains formatting options for the values returned from date, datetime, and currency properties.
- The
date and dateTime objects can include format and relative subfields:
format (string): a date or datetime string like MM-DD-YYYY or MM-DD-YYYY:mm:ss. Supports standard date time string formats.
relative (boolean): set to true to display the amount of time passed since the returned value (e.g., (1 day ago) or (1 hour ago)).
- The
currency object can include addSymbol (boolean), which sets whether the currency symbol should display with the number. Set to true to display the currency symbol.
Please note: formatting is applied based on property type (date, datetime, currency) rather than content. For example, date formatting will not apply to a string type property containing a date value.
{
isLoading: false,
error: null,
results: [
{
"objectId": 123456,
"properties": {
"email": "john@example.com",
"firstname": "John",
"lastname": "Smith"
}
}
],
total: 342,
pagination: {
"hasNextPage": true,
"hasPreviousPage": false,
"currentPage": 1,
"pageSize": 10,
"nextPage": () => {}, // function to fetch results for next page
"previousPage": () => {}, // function to fetch results for previous page
"reset": () => {} // function to reset to page one of results
},
isRefetching: false,
refetch: () => {} // function to refetch the latest search results
}
Indicates whether the data is being fetched.
For failed fetch requests, an object with error details. Will be null for successful requests.
The matching CRM records for the current page. Each item includes:
objectId: the ID of the CRM record.
properties: an object containing the requested property data, listed in alphabetical order.
The total number of matching records across all pages.
An object with pagination utilities, including:
hasNextPage: a boolean indicating if more pages are available.
hasPreviousPage: a boolean indicating if previous pages exist.
currentPage: the current page number.
pageSize: the number of items per page.
nextPage(): the function to go to the next page.
previousPage(): the function to go to the previous page.
reset(): the function to reset to the first page.
Indicates whether a refetch request is in progress.
A function to refetch the latest search results, with formatting applied as specified in the original hook call. The current page will be preserved.
import {
hubspot,
Text,
Button,
Flex,
useCrmSearch
} from "@hubspot/ui-extensions";
const Extension = () => {
const { results, total, isLoading, error, pagination, isRefetching, refetch } = useCrmSearch(
{
objectType: 'contact',
properties: ['firstname', 'lastname', 'email'],
filterGroups: [{
filters: [
{ propertyName: 'lifecyclestage', operator: 'EQ', value: 'lead' }
]
}],
sorts: [{ propertyName: 'createdate', direction: 'DESCENDING' }],
pageLength: 10,
}
);
if (isLoading) {
return <Text>Loading results...</Text>;
}
if (isRefetching) {
return <Text>Refetching results...</Text>;
}
if (error) {
return <Text>Error loading results: {error.message}</Text>;
}
return (
<Flex direction="column" gap="medium">
<Text>{total} records found. Displaying page {pagination.currentPage}.</Text>
{results.map(record => (
<Flex direction="column" key={record.objectId}>
<Text>
{record.properties.firstname} {record.properties.lastname}: {record.properties.email}
</Text>
</Flex>
))}
<Flex direction="row" gap="sm">
<Button
onClick={pagination.previousPage}
disabled={!pagination.hasPreviousPage}
variant="secondary"
>
Previous page
</Button>
<Button
onClick={pagination.nextPage}
disabled={!pagination.hasNextPage}
variant="primary"
>
Next page
</Button>
<Button
onClick={pagination.reset}
variant="secondary"
>
Reset to first page
</Button>
<Button
onClick={refetch}
variant="primary"
>
Fetch latest data
</Button>
</Flex>
</Flex>
);
};
hubspot.extend(() => <Extension />);
import { useState } from 'react';
import { hubspot, Input, Text, useCrmSearch, useDebounce } from '@hubspot/ui-extensions';
const Extension = () => {
const [query, setQuery] = useState('');
// Waits for the user to stop typing before triggering a new search request
const debouncedQuery = useDebounce(query);
const { results, isLoading } = useCrmSearch({
objectType: 'contact',
properties: ['firstname', 'lastname', 'email'],
query: debouncedQuery,
});
return (
<>
<Input
value={query}
onInput={setQuery}
placeholder="Type to search..."
label="Search Contacts"
name="searchContacts"
/>
{isLoading && <Text>Loading...</Text>}
{results.map(record => (
<Text key={record.objectId}>
{record.properties.firstname} {record.properties.lastname}
</Text>
))}
</>
);
};
hubspot.extend(() => <Extension />);
import React from 'react';
import { hubspot, Text, useCrmSearch, useExtensionContext } from '@hubspot/ui-extensions';
const Extension = () => {
const { crm } = useExtensionContext();
const { results, isLoading } = useCrmSearch(
{
objectType: 'deal',
properties: ['dealname', 'amount', 'dealstage'],
filterGroups: [
{
filters: [
{
// 'associations.{objectType}' is a pseudo-property that filters by association
propertyName: 'associations.contact',
operator: 'EQ',
value: crm.objectId,
},
],
},
],
},
{
propertiesToFormat: 'all',
formattingOptions: {
currency: {
addSymbol: true,
},
},
},
);
if (isLoading) return <Text>Loading associated deals...</Text>;
return (
<>
{results.map((deal) => (
<React.Fragment key={deal.objectId}>
<Text>{deal.properties.dealname}</Text>
<Text>Amount: {deal.properties.amount}</Text>
<Text>Stage: {deal.properties.dealstage}</Text>
</React.Fragment>
))}
</>
);
};
hubspot.extend(() => <Extension />);
CRM-specific hooks
These hooks are only available in CRM extension points (crm.record.tab, crm.record.sidebar, crm.preview, and helpdesk.sidebar).
import {
useCrmProperties,
useAssociations
} from "@hubspot/ui-extensions/crm";
useCrmProperties
The useCrmProperties hook fetches properties from the current CRM record with optional formatting. It accepts an array of properties to fetch, along with an optional object to format the returned data.
Call
Response
Example usage
useCrmProperties(["firstname", "lastname", "email"], {
propertiesToFormat: "all",
formattingOptions: {
date: {
format: "MM-DD-YYYY",
relative: false,
},
dateTime: {
format: "MM-DD-YYYY hh:mm",
relative: false,
},
currency: {
addSymbol: true,
},
},
});
Either 'all' or an array of property names to format.
Contains formatting options for the values returned from date, datetime, and currency properties.
- The
date and dateTime objects can include format and relative subfields:
format (string): a date or datetime string like MM-DD-YYYY or MM-DD-YYYY:mm:ss. Supports standard date time string formats.
relative (boolean): set to true to display the amount of time passed since the returned value (e.g., (1 day ago) or (1 hour ago)).
- The
currency object can include addSymbol (boolean), which sets whether the currency symbol should display with the number. Set to true to display the currency symbol.
Formatting is applied based on property type (date, datetime, currency) rather than content. For example, date formatting will not apply to a string type property containing a date value.
{
isLoading: false,
error: null,
properties: {
"firstname": "Joe",
"lastname": "Smith",
"email": "joe@joebiz.com"
},
isRefetching: false,
refetch: () => {} // function to refetch properties
}
Indicates whether the data is being fetched.
For failed fetch requests, an object with error details. Will be null for successful requests.
An object with key-value pairs of returned properties, as formatted by formattingOptions. Properties are returned in alphabetical order.
Indicates whether a refetch request is in progress.
A function to refetch the latest property values, with formatting applied as specified in the original hook call.
import {
hubspot,
Text,
Button
} from "@hubspot/ui-extensions";
import { useCrmProperties } from "@hubspot/ui-extensions/crm";
const Extension = () => {
const { properties, isLoading, error, refetch, isRefetching } = useCrmProperties(
// Array of properties to return
['firstname', 'lastname', 'email'],
// Optional formatting options for returned data
{
propertiesToFormat: 'all',
formattingOptions: {
date: {
format: 'MM-DD-YYYY',
relative: false
},
dateTime: {
format: 'MM-DD-YYYY hh:mm',
relative: false
},
currency: {
addSymbol: true
}
}
}
);
if (isLoading) {
return <Text>Loading properties...</Text>;
}
if (isRefetching) {
return <Text>Refetching properties...</Text>;
}
if (error) {
return <Text>Error loading properties: {error.message}</Text>;
}
return (
<>
<Text>
The contact is "{properties.firstname} {properties.lastname}"
with email "{properties.email}".
</Text>
<Button onClick={refetch} variant="primary">
Refetch property data
</Button>
</>
);
};
hubspot.extend(() => <Extension />);
useAssociations
The useAssociations hook fetches CRM records of a specific object type associated with the currently displaying record. It accepts an object containing configuration details for the association fetch request, and an optional object that formats returned property data.
Call
Response
Example usage
useAssociations(
{
// Object type ID to fetch associations for
toObjectType: "0-1",
// Optional properties to fetch from associated records
properties: ["firstname", "lastname", "email", "phone"],
// Optional pagination settings
pageLength: 25,
},
// Optional formatting configuration (same as useCrmProperties)
{
propertiesToFormat: "all",
formattingOptions: {
date: {
format: "MM-DD-YYYY",
relative: false,
},
dateTime: {
format: "MM-DD-YYYY hh:mm",
relative: false,
},
currency: {
addSymbol: true,
},
},
}
);
Configures the association data fetch request with:
toObjectType: the object type ID to fetch associations from (e.g., ‘0-1’ for contacts).
properties: an optional array of properties to fetch from associated records.
pageLength: an optional number of items per page (defaults to 10).
Either 'all' or an array of property names to format.
Contains formatting options for the values returned from date, datetime, and currency properties.
- The
date and dateTime objects can include format and relative subfields:
format (string): a date or datetime string like MM-DD-YYYY or MM-DD-YYYY:mm:ss. Supports standard date time string formats.
relative (boolean): set to true to display the amount of time passed since the returned value (e.g., (1 day ago) or (1 hour ago)).
- The
currency object can include addSymbol (boolean), which sets whether the currency symbol should display with the number. Set to true to display the currency symbol.
{
isLoading: false,
error: null,
results: [
{
"toObjectId": 70284463640,
"associationTypes": [
{
"category": "HUBSPOT_DEFINED",
"typeId": 449,
"label": ""
}
],
"properties": {
"email": "emailmaria@hubspot.com",
"firstname": "Maria",
"lastname": "Johnson (Sample Contact)"
}
}
],
pagination: {
"hasNextPage": true,
"hasPreviousPage": false,
"currentPage": 1,
"pageSize": 1,
"nextPage": () => {}, // function to fetch results for next page
"previousPage": () => {}, // function to fetch results for previous page
"reset": () => {} // function to reset to page one of results
},
isRefetching: false,
refetch: () => {} // function to refetch the latest associations data
}
Indicates whether the data is being fetched.
For failed fetch requests, an object with error details. Will be null for successful requests.
Association details of the returned CRM records. Includes the following fields:
toObjectId: the ID of the CRM record.
associationTypes: an array of association type information.
properties: an object containing the requested property data, listed in alphabetical order.
An object with pagination utilities, including:
hasNextPage: a boolean indicating if more pages are available.
hasPreviousPage: a boolean indicating if previous pages exist.
currentPage: the current page number.
pageSize: the number of items per page.
nextPage(): the function to go to next page.
previousPage(): the function to go to previous page.
reset(): the function to reset to first page.
Indicates whether a refetch request is in progress.
A function to refetch the latest associations data, with formatting applied as specified in the original hook call. The current page will be preserved.
import {
Text,
Button,
Flex,
hubspot
} from "@hubspot/ui-extensions";
import { useAssociations } from "@hubspot/ui-extensions/crm";
const Extension = () => {
const { results, error, isLoading, pagination, isRefetching, refetch } = useAssociations(
{
// Object type ID to fetch associations for
toObjectType: '0-1',
// Optional properties to fetch from associated objects
properties: ['firstname', 'lastname', 'email', 'phone'],
// Optional pagination settings
pageLength: 25,
},
// Optional formatting configuration (same as useCrmProperties)
{
propertiesToFormat: 'all',
formattingOptions: {
date: {
format: 'MM-DD-YYYY',
relative: false
},
dateTime: {
format: 'MM-DD-YYYY hh:mm',
relative: false
},
currency: {
addSymbol: true
}
}
}
);
if (isLoading) {
return <Text>Loading associations...</Text>;
}
if (isRefetching) {
return <Text>Refetching associations...</Text>;
}
if (error) {
return <Text>Error loading associations: {error.message}</Text>;
}
return (
<Flex direction="column" gap="medium">
<Text>Associations (Page {pagination.currentPage})</Text>
{results.map((association, index) => (
<Flex direction="column" key={association.toObjectId}>
<Text>Association {index + 1}: Object ID {association.toObjectId}</Text>
<Text>
Association Types: {association.associationTypes.map(type => type.label).join(', ')}
</Text>
{Object.entries(association.properties).map(([key, value]) => (
<Text key={key}>{key}: {value || 'N/A'}</Text>
))}
</Flex>
))}
<Flex direction="row" gap="sm">
<Button
onClick={pagination.previousPage}
disabled={!pagination.hasPreviousPage}
variant="secondary"
>
Previous Page
</Button>
<Button
onClick={pagination.nextPage}
disabled={!pagination.hasNextPage}
variant="primary"
>
Next Page
</Button>
<Button
onClick={pagination.reset}
variant="secondary"
>
Reset to First Page
</Button>
<Button
onClick={refetch}
variant="primary"
>
Fetch Latest Data
</Button>
</Flex>
</Flex>
);
};
hubspot.extend(() => <Extension />);
Best practices
Always use TypeScript generics
// ✅ Good - Typed for better IntelliSense and type safety
const actions = useExtensionActions<'crm.record.tab'>();
const context = useExtensionContext<'crm.record.tab'>();
// ❌ Avoid - Less type safety and IntelliSense
const actions = useExtensionActions();
const context = useExtensionContext();
// ✅ Good - Hooks at component level
function MyExtension() {
const { addAlert } = useExtensionActions<'crm.record.tab'>();
const context = useExtensionContext<'crm.record.tab'>();
const handleClick = () => {
addAlert({ message: `Action from ${context.location}` });
};
return <Button onClick={handleClick}>Click me</Button>;
}
// ❌ Avoid - Don't call hooks in event handlers
function MyExtension() {
const handleClick = () => {
const { addAlert } = useExtensionActions(); // Wrong!
addAlert({ message: "Hello" });
};
return <Button onClick={handleClick}>Click me</Button>;
}