HubSpot UI extensions enable you to customize HubSpot’s UI to suit your needs. UI extensions can be created for CRM records and the ticket panels within help desk, and they can interact with both HubSpot and external data using a private or public app included in the project.
Below, learn more about how UI extensions work, including how they fetch data, along with best practices.
UI extensions are built using React as a primary front-end framework. For UI extensions in private apps, the back-end is provided by HubSpot using serverless functions for operations such as fetching data. For UI extensions in public apps, you'll need to provide a custom back-end to handle OAuth authentication.
Extensions are built locally using the HubSpot CLI, which you'll use to run a HubSpot local development server for the front-end and deploy back-end changes to the HubSpot account. Extensions are powered by the UI extensions SDK, which offers a number of utilities, including UI components to create the visual elements of the extension. The UI extensions SDK is published as an npm package, which you'll include in the package.json
file within the extensions
directory.
The UI extensions SDK enables you to:
-
Register an extension with HubSpot
-
Run serverless functions (private apps only)
-
Add actions to your extension, such as opening an iframe in a modal, adding success and failure alerts, and fetching CRM properties
-
Build the extension's UI with components
While an Enterprise account is needed to create UI extensions for private apps, a paid seat is not required to view UI extensions or custom tabs on CRM records. Any user in the account will be able to view and use UI extensions once uploaded to the account.
Within the project structure, UI extensions are stored in a /src/app/extensions
directory. The extension then uses UI components to render a UI and display information retrieved by the app’s serverless function. UI components are provided by the UI Extension SDK and can be customized through their included fields. Because you’re using React, you can also create your own component building blocks by packaging these components and importing them as needed.
At a high level, a React-based UI extension consists of:
- A back-end: for private apps, the back-end is provided for you through serverless functions. The
app.functions
directory contains the serverless function JavaScript file and a JSON configuration. You can also add apackage.json
file to include any needed dependencies. The serverless function sends and fetches data that your React components can later use. For public apps, this directory is not needed, as you'll provide your own back-end. - React front-end: an extensions directory containing
.jsx
or.tsx
files to render components, along with the app card’s.json
configuration and a requiredpackage.json
that includes any needed dependencies.
As shown in the screenshot above, the front-end and back-end directories both include their own package.json
file. You can use these files to include dependencies as needed.
UI extensions can be created for a variety of locations and CRM objects in HubSpot. An extension can only appear in one location, but can be appear in that location for multiple CRM objects.
UI extensions can be built for the following locations:
-
The middle column of CRM record pages
-
The right sidebar of CRM record pages
-
The CRM record preview panel that appears on the right side throughout HubSpot
-
The ticket sidebars within the help desk tool (both the preview sidebar and the help desk ticket record view)
Learn more about extension locations.
UI extensions can be built for the following CRM objects:
- Contacts
- Companies
- Deals
- Tickets
- Custom objects
In addition, the following CRM objects are supported if you've enabled the corresponding data model template (BETA):
- Appointments and services (Healthcare template)
- Courses (Education template)
- Listings (Real estate template)
Learn more about compatible objects.
You can fetch data in multiple ways, depending on the data source:
- To fetch third-party data, you can make API requests authenticated by secrets.
- To fetch HubSpot data, you can:
- use the
fetchCrmObjectProperties
action to fetch data from the currently displaying CRM record. - use HubSpot's API endpoints to fetch data outside of the currently displaying CRM record.
- use GraphQL to query CRM data directly.
- use the
To fetch data with a private app, you'll use serverless functions. To fetch data with a public app, you'll use the hubspot.fetch API.
- For private apps:
- You can fetch data from external APIs using an API client, such as Axios or other third-party clients, in your serverless functions. To authenticate these requests, learn how to create and use secrets in serverless functions. To see an example of using a serverless function to fetch data from a third-party source, check out the mapbox-api code sample extension.
- You can fetch data from HubSPot's APIs using serverless functions, but instead of authenticating with a secret, you'll use the private app's access token. Learn more about authenticating HubSpot API calls with private app access tokens.
- For public apps, you can fetch data using the hubspot.fetch API. You'll need to provide authentication through your self-hosted back-end.
To fetch data from the HubSpot account, such as displaying property data from the record you’re viewing, you’ll pass the fetchCrmObjectProperties
method to the extension via hubspot.extend()
in your React files. This method automatically handles authentication, so you don't need to include a private app access token. Learn more about the fetchCrmObjectProperties method.
hubspot.extend(({ actions }) => (
<HelloWorld fetchProperties={actions.fetchCrmObjectProperties} />
));
const HelloWorld = ({ fetchProperties }) => {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
useEffect(() => {
fetchProperties(['firstname', 'lastname']).then((properties) => {
setFirstName(properties.firstname);
setLastName(properties.lastname);
});
}, [fetchProperties]);
return (
<Text>
Hello {firstName} {lastName}
</Text>
);
};
In addition to using fetchCrmObjectProperties
, CRM data components can fetch and visualize HubSpot data out of the box. You can also use GraphQL to query CRM data through the /collector/graphql
endpoint. Learn more about querying CRM data using GraphQL.
For security, the React front-end cannot fetch data directly with APIs. For private apps, you can fetch data on the back-end using serverless functions. Then, on the front-end you'll use the hubspot.serverless
API to call the serverless function and send your JSON payload as parameters. Learn more about serverless functions.
Because UI extensions are split between front-end and back-end, you can call multiple serverless functions from the same card. Or, you can reuse the same serverless function to run different operations and pass them as needed to the front-end.
If your serverless function requires secrets, learn more about managing secrets for deployed serverless functions and local development.
To render UI extensions, HubSpot uses sandboxed iframes for isolation, web workers for untrusted code execution, and Shopify's remote-ui library for UI abstraction. This enables you to build with familiar tools like React and JavaScript, and remote-ui
translates that into specific components to the host via postmessage
. This process is essentially serializing a React element tree and sending it through postmessage
for the host to evaluate. This results in benefits, such as:
- Helping to protect both you and HubSpot with a sound and resilient solution for isolated code execution, while ensuring a consistent in-app user experience through the use of HubSpot's component library.
- Providing you with a productive and delightful developer experience by leveraging mainstream frameworks like React.
However, it also separates the UI extensions from typical web applications built with React. Below, learn more about some of differences you can expect when developing React-based UI extensions.
A project cannot contain file paths that exceed 500 characters. If your project includes a file whose path exceeds that limit, you'll receive the following error in the console when uploading your project:
"[ERROR] File paths must not exceed 500 characters. Shorten the following file path, and then try again. {Relevant erroring file path}."
When building UI extensions on HubSpot, you can only use the components provided through the UI extensions SDK. Each component has a set of parameters that you can use for customization, but these components cannot be customized beyond those parameters.
Because UI extensions are rendered through sandboxed iframes, they cannot directly access the DOM. This restriction means that common methods of DOM manipulation and event listening, such as document.getElementById
or document.addEventListener
, are unavailable within the iframe's local script context. However, some components provide callback functions that you can use for certain events, like clicks, focus, and input.
- React Router's
useNavigate()
hook will not function as expected within a UI extension. This hook manipulates the browser's window object to change the URL, which is not allowed within a sandboxed iframe. - The
useForm()
hook from react-hook-form is not supported because it relies on creating event listeners directly on DOM elements for form validation and submission, which is not allowed within a sandboxed iframe.
Because you can't access the DOM and components don't pass the style
prop to the renderer, you can only style components with the provided component props.
You cannot make client-side HTTP requests through UI extensions, meaning browser features like fetch
and XMLHttpRequest
will not work, nor will libraries like Axios, which is built around those features. Instead, requests should be made through the serverless function, executed by HubSpot behind the scenes.
Requests made within the sandbox do not contain cookies, so rather than trying to store session information with cookies, you can look to the context
object, which is passed to the extension component via hubspot.extend
. This object contains information related to the authenticated user and HubSpot account. Learn more about fetching account and user data.