Component design guidelines (BETA)
Sales Hub
- Enterprise
Service Hub
- Enterprise
When creating a UI extension, you'll include HubSpot-provided components to render various UI elements for interaction and displaying data. When using these components, you should follow the guidelines below to create a more expected, seamless user experience. These guidelines follow the same principles that HubSpot uses when designing UI elements, which will help your extension feel more like a native functionality.
To view the code and prop definitions for each component, check out the UI extension components reference guide. Each component below will also contain a link out to its respective code reference.
Below you'll find guidance for the following components:
Standard components
The Alert
component should give usage guidance, notify users of action results, or warn them about potential issues or failures. Alerts can be placed in components statically or triggered dynamically as the result of an action.
- Title: the alert's bolded title text which should summarize its intent or an action outcome.
- Body: the descriptive text that follows the title, which should provide the user with the necessary information to proceed.
- Variant: the color of the alert. Learn more about when to use each variant below.
Styles
There are four alert types, which can be set using the variant
prop:
- Info: a blue alert for general tips and messages that guide the user through a specific task.
- Success: a green alert to indicate the successful completion of a task or to convey a positive CRM record status.
- Warning: a yellow alert for general warnings related to the performance of the system or the status of the CRM record.
- Error/danger: a red alert indicating an error, the degradation of a system, or a negative CRM record status. You should not use this alert type for anything other than errors and negative statuses.
- Use an info alert to add instructions to a custom card, such as "Fill out this form using the contact's company information."
- Use a success alert when a user successfully submits a form in the UI extension, or to convey that a contact qualifies for enrollment in a promotional offering.
- Use a warning alert to call attention to a contact's upcoming renewal date.
- Use an error or danger alert to notify the user of a form submission error or to signify that a high-urgency ticket has lapsed.
- DO: use alert text that clearly communicates to the user what they need to know.
- DO: make alert text actionable, especially for error alerts which should provide a clear path to resolution.
- DO: use proper punctuation, such as periods and question marks at the end of the descriptive text.
- DO: set the alert as the first component in the extension when possible for visibility. Extensions with alerts should also be placed at the top of the CRM record by a HubSpot admin. Learn more about customizing CRM record tabs.
- DON'T: use exclamation points in warning or error alerts. Exclamation points should only be used for short, positive messages (e.g. "Great!" or "Well done!").
The Button
component should give users a way to perform actions, such as submitting a form, sending data to an external system, or deleting/disconnecting data related to a CRM record.
- Button text: the visible button text that describes the button's action.
- Variant: the color of the button. Learn more about when to use each type below.
- Primary: a dark blue button for the most frequently used or most important action on an extension. Each extension should only have one primary button.
- Secondary: a grey button to provide alternative or non-primary actions. Each extension should include no more than two secondary buttons.
- Destructive: a red button for actions that delete, disconnect, or perform any action that the user can't undo. Button text should clearly communicate what is being deleted or disconnected. After a destructive button is clicked, the user should have to verify or confirm the action.
- Disabled: a greyed out button that cannot be clicked. This button state is set with the
disabled
prop, not thevariant
prop.
- Use a primary button at the end of a form to submit data to another system.
- Use a secondary button next to a primary form submit button to reset form fields.
- Use a destructive button to enable users to delete a contact's data from an external system.
- Set a button to disabled when a contact doesn't qualify for a form submission due to missing criteria or other ineligibility.
- DO: set button text that clearly communicates what action will occur when a user clicks it. Text should be unambiguous and concise (~2-4 words).
- DO: use sentence-casing for button text (only the first word capitalized)
- DO: minimize the number of buttons that appear on a page record across all extensions.
- DON'T: include multiple primary buttons in a single extension.
- DON'T: use a destructive button unless the consequences are significant or irreversible.
The ButtonRow
component renders two or more individual Button components in a row. The buttons within the row follow the same guidelines as the Button component above.
- Primary button: only use one per extension.
- Secondary button: only use with a primary and/or destructive button.
- Destructive button: only use for actions that are destructive, paired with a secondary button.
- A primary and secondary button in a row to progress through a multi-step form.
- A destructive and secondary button in a row to confirm and cancel a contact deletion.
- DO: include a secondary button with a destructive button to allow users to cancel the action.
- DON'T: use multiples of the same button type in a row. For example, don't include more than one primary button in one row.
- DON'T: use more than two secondary buttons in a single extension.
- DON'T: use more than three buttons in a row.
Use the DescriptionList
component to display pairs of labels and values in a way that's easy to read at a glance.
- Label: describes the information being displayed.
- Value: the information to display.
By default, list items will be stacked vertically. You can use the the direction
prop to stack them horizontally.
row
:column
(default):
- Display easy to scan information for a sales rep to use on a call.
- Highlight the most recently updated properties on a company record.
- DO: keep copy succinct, ideally one word each for the label and value.
- DO: use the horizontal orientation for horizontal layouts, and vertical orientation for column layouts.
- DON'T: use this component to display long strings of text.
- DON'T: use this component for lists that you want to be editable in the UI.
The Divider
component adds a grey, horizontal line for spacing out components vertically or creating sections in an extension. Use this component to space out other components when the content needs more separation than white space.
Using the distance
prop, you can set the amount of padding above and below the divider. Values range from extra-small
to extra-large
(flush
by default).
- DO: use dividers to group similar components together.
- DO: consider when a new card or component might be needed, rather than using a divider.
- DON'T: use two dividers in a row without content between them.
The EmptyState
component sets the content that appears when the extension is in an empty state. Use this component when there's no content or data to help guide users.
- Image: the default image that comes with the component.
- Title: the title that describes why the component is in an empty state.
- Additional text: an additional
Text
component to provide further guidance. This does not come with the component by default. - Additional button: an additional
Button
component to can help users take action.
- Display when it's the first use of a feature.
- Show when the user is required to take action in order to populate the card with information.
- DO: make empty states informative so that users understand what will appear when the extension is not empty.
- DO: make empty states actionable. If relevant, explain the benefits of this area and how to add content or data.
- DON'T: make empty states too long.
The ErrorState
component communicates extension errors.
- Illustration: one of three default error illustrations.
- Title: the main error message to explain the root cause if known.
- Additional text: an additional
Text
component to provide further guidance. This does not come with the component by default. - Additional button: an additional
Button
component to can help users take action.
- Known cause: [what failed] + [why it failed] + [next steps]. For example, Failed to load extension due to outage, please wait a few minutes and try again.
- Unknown cause: [what failed] + [next steps]. For example, Couldn't load data, try refreshing the page or contacting IT.
Using the type
prop, you can set one of three illustrations:
error
:support
:lock
:
- Use the
default
error type when a card encounters an error when fetching data. - Use the
support
error type when the user should contact internal or external support to resolve an error. - Use the
lock
error type when the user needs to log in or doesn't have permission to access the card's data.
- DO: use text that's clear, direct, brief, and helpful.
- DON'T: use technical jargon.
- DON'T: say "sorry" or use frivolous language such as "oops," "uh-oh," and "it's us, not you."
- DON'T: use exclamation points.
the Form
component renders a submittable form containing other form-related elements, such as inputs and a submit button.
- Label: the input label.
- Text input: a text input with a placeholder value of "First name."
- Label: the select input label.
- Select input: a select input with value of "Customer."
- Button: a button to submit the form's information.
- A form to submit customer information to an external database.
- A form to place a product order on behalf of a customer.
- DO: include text inputs when a user should be able to submit any value.
- DO: include select inputs when a user should only be able to select from a set of values.
- DO: include descriptions and placeholder text to provide context to users.
- DO: always position the submit button at the bottom of the form.
- DON'T: include a form without a submit button.
The Heading component renders large text for titles.
- DO: use headers to give users a summary of the information that the extension contains.
- DON'T: use more than one heading for each page or section in the extension.
- DON'T: use headers for paragraphs or long sentences.
The Image
component should be used to add a logo or other visual brand identity asset, or to accentuate other content in the extension. Images cannot exceed the width of the extension's container at various screen sizes, and values beyond that maximum width will not be applied to the image.

- DO: use images with a predominantly horizontal aspect ratio for scalability (e.g. 21:9, 16:9, 4:3) for scalability.
- DO: balance image quality and page performance. Images should be high enough quality to serve their purpose, but should not exceed 2-3MBs.
- DO: include alt-text in every image (set by the
alt
prop) for accessibility. - DON'T: use images containing text that can only be interpreted by sighted users.
- DON'T: use images that contain hateful, violent, or profane language and/or visuals.
The Input
component renders a text input field where users can enter a custom text value. Like other inputs, this component should only be used within a form that has a submit button.
- Label: the input's label.
- Description: the text that describes the field's purpose.
- Placeholder: the placeholder value that displays when no value has been entered.
- Required field indicator: communicates to the user that the field is required for form submission.
- Tooltip: on hover, displays additional information about the field.
Use for form fields where a user can enter any value, such as email address or name.
- DO: make label and description text concise and clear.
- DO: include placeholder text to help users understand what's expected in the field.
- DO: indicate if a field is required.
- DO: include clear validation error messages so that users know how to fix errors.
- DON'T: use this component for long responses, such as open-ended comments or feedback. Instead, use the text area input.
- DON'T: use placeholder text for critical information, as it will disappear once users begin to type. Critical information should be placed in the label and descriptive text, with additional context in the tooltip if needed.
The NumberInput
component renders an field for entering number values. Like other inputs, this component should only be used within a form that has a submit button.
- Label: the input's label.
- Description: the text that describes the field's purpose.
- Value: an entered value.
A field where salespeople can enter the total deal amount.
- DO: make label and description text concise and clear.
- DO: include placeholder text to help users understand what's expected in the field.
- DO: indicate if there is a minimum or maximum number requirement.
- DO: indicate if a field is required.
- DO: include clear validation error messages so that users know how to fix errors.
- DON'T: use this component for long responses, such as open-ended comments or feedback. Instead, use the text area input.
- DON'T: use placeholder text for critical information, as it will disappear once users begin to type. Critical information should be placed in the label and descriptive text, with additional context in the tooltip if needed.
The Select
input component renders a dropdown select field where a user can select a single value. A search bar will be automatically included when there are more than seven options. Like other inputs, this component should only be used within a form that has a submit button.
- Label: the label that describes the field's purpose.
- Value: the field's selected value.
Using the variant
prop, you can render the input with standard styling or transparent styling:
variant="input"
(default):variant="transparent"
:
- When
readOnly
is set totrue
, the field will be greyed out and uneditable. - When you include a
placeholder
value, that value will display by default. Otherwise, the field will display Select by default.
Use this type of field when there are a range of set options to choose from, such as:
- A list of products that can be purchased.
- A list of office locations to ship to.
- A list of delivery options for a vendor.
- DO: make label and description text concise and clear.
- DO: indicate if a field is required.
- DO: include clear validation error messages so that users know how to fix errors.
- DO: include placeholder text to help users understand what's expected in the field.
- DON'T: use this component when you want users to be able to select multiple options. Instead, use the multi-select input component.
- DON'T: use placeholder text for critical information, as it will disappear once users begin to type. Critical information should be placed in the label and descriptive text, with additional context in the tooltip if needed.
The TextArea
input component renders a text field where users can enter longer strings of text. Using props, you can customize the size of the field along with the maximum number of characters. Like other inputs, this component should only be used within a form that has a submit button.
- Label: the field's label.
- Description: the text that describes the field's purpose.
- Value: an entered value.
- Required field indicator: communicates to the user that the field is required for form submission.
- Tooltip: on hover, displays additional information about the field.
A field where salespeople can leave comments after meeting a new client.
- DO: make label and description text concise and clear.
- DO: indicate if a field is required.
- DO: include clear validation error messages so that users know how to fix errors.
- DO: include placeholder text to help users understand what's expected in the field.
- DO: indicate if there is a character limit.
- DON'T: use this field for short values, such as names, numbers, and dates.
- DON'T: use placeholder text for critical information, as it will disappear once users begin to type. Critical information should be placed in the label and descriptive text, with additional context in the tooltip if needed.
The Link
component renders a clickable hyperlink. Links might take users to another page or part of the HubSpot app, or function as buttons.
- Link text: text that describes where the link leads.
- External link icon: indicates that the link will lead to a page outside of the HubSpot app.
Using the variant
prop, you can set the following styling:
primary
: the default blue (#0091ae).light
: a white link that turns to a lighter shade of blue on hover (#7fd1de).dark
: a darker shade of blue (#33475b).destructive
: a red link (#f2545b).
- Use the default
primary
variant when you want to link to another page or contact record in HubSpot. - Use the
light
variant when you want to include a link on a dark background. - Use the
dark
variant to include a link in an alert. - Use the
destructive
variant when the link results in an action that can't be undone by the user, such as deleting contact property information.
- DO: space out links so that users can tell when they'll be navigation to a different place.
- DO: make link text concise and contextual.
- DO: use the
destructive
variant sparingly and only when the action can't be undone. - DON'T: crowd multiple links together.
- DON'T: use the
dark
variant outside of alerts.
The LoadingSpinner
component renders a visual indicator that the card is loading or processing information.
- Label: the text that describes the loading state.
- Size: the size of the component. From left to right: extra small (
xs
), small (sm
, default), medium (md
). - Layout: the positioning of the spinner. From left to right:
inline
,centered
.
- A loading state after the user submits form data to an external system (e.g., "Submitting contact details").
- A loading state as the card retrieves customer purchase history from an external system (e.g., "Loading purchase history").
- DO: keep label text as concise as possible.
- DO: use label text to describe what's happening during the loading process.
- DO: use complete sentences in label text.
- DON'T: include multiple loading spinners at once in a single card to avoid confusion.
The ProgressBar
component renders a visual representation of data in motion towards a positive or negative target.
- Title: the text that displays above the bar to describe the data it represents.
- Completion percentage: the percent value of how much progress has been made.
- Value description: the text that describes the current state of the bar's value.
- Variant: the color of the progress bar.
Using the variant prop, you can set the following progress bar colors:
success
: a green colored bar to indicate movement towards a positive goal or outcome.warning
: a yellow colored bar to indicate movement towards a negative outcome or limitation.danger
: a red colored bar to indicate movement towards an extremely negative outcome or when a limitation has been reached.
- Evaluating the sale of products against a quota or goal.
- Communicating the stage progress of a deal or ticket.
- Monitoring the number of support calls or tickets per customer.
- DO: use the
showPercentage
prop to give the user more information about the status of the progress. - DON'T: use more than 3-4 progress bars in a single card.
The Statistics
component renders a visual spotlight of one or more data points. Includes the StatisticItem
and StatisticTrend
components.
- StatisticItem label: the
statisticItem
's label text. - StatisticItem number: the
statisticItem
's primary number. - StatisticTrend value: the percentage trend value.
- StatisticTrend direction: the direction if the trend arrow (up or down).
In StatisticsTrend
components, use the direction
prop to describe whether the data is trending up or down.
increase
: for additions or positive progression for a given time period.decrease
: for subtractions or negative progression for a given time period.
- Calling out the progress of quarterly sales for a company.
- Monitoring the amount of traffic and social media engagement that a contact has for the month.
- DO: keep statistics labels short and concise.
- DO: place statistics components towards the top of a card when possible to enable users to more easily scan information without scrolling.
- DON'T: include more than three statistics components per card if possible.
- DON'T: use more than four statistics components side by side.
- DON'T: include sensitive data that you don't want all users to see.
The Table
component renders a table that displays information in columns and rows. To build a table, you'll need to include the following other components:
TableHead
: the header section of the table containing column labels.TableRow
: individual table rows.TableHeader
: cells containing bolded column labels.TableBody
: container for the main table contents (rows and cells).TableCell
: individual cells within the main body.
- Table head: the header row of the table containing table headers that label each column.
- Table row: an individual table row containing table cells.
- Table column: an individual column, consisting of a table header and table cells in each row.
- Table cell: an individual cell containing data.
- Pagination: table pagination to navigate through contents.
- A client list containing names, phone numbers, job positions, and email addresses that salespeople can use to prioritize outreach.
- A summary table of the deals closed last quarter.
- DO: keep text within data cells clear and concise for easier scanning.
- DO: always include a table header row to label columns.
- DO: limit the use of links in table cells.
- DON'T: use multiple tables on one screen when possible.
- DON'T: render a blank table if there's no potential for no data to display, such as with search or filters. Instead, use the empty state component when there are no results to display.
The Tag
component renders a tag to label or categorize information or other components. Tags can be static or clickable for invoking functions.
- Variant: the color of the tag.
- Tag text: the text that communicates the tag's purpose.
Using the variant
prop, you can choose from one of four tag colors:
default
(default): for general tagging and labeling.success
: for indicating or confirming the success of an action.warning
: for indicating something that might be time-sensitive or of importance.error
: for indicating error or failure.
- Use a default tag to indicate that a customer is active
- Use a success tag to indicate that an item in a to-do list has been completed.
- Use a warning tag to indicate that a deal is expiring soon.
- Use an error tag to indicate that an error happened when trying to sync a specific property in a table.
- DO: make tag text concise and clear.
- DO: ensure that tag variants are used consistently across the extension.
- DON'T: use tags in place of buttons or links.
- DON'T: rely on color alone to communicate the tag's meaning. Ensure that tag text is clear and helpful.
The Text
component renders text with formatting options.
Using the format
prop, you can style text with a number of options:
{ fontWeight: 'bold' }
: sets the text to bold.{ fontWeight: 'demibold' }
: sets the text to a lighter bold.{ italic: true }
: sets the text to italics.{ lineDecoration: 'strikethrough' }
: adds a strikethrough to the text.{ lineDecoration: 'underline' }
: underlines the text.<Text inline={true}>
: enables you to set text styling within the same line by adding more text without breaking the line. For example:<Text>Text <Text inline={true} format=> with inner bold.</Text>
.
You can also control text size with the variant
prop.
variant="bodytext"
(default)variant="microcopy"
- Use body text when you want to display a summary of the last call with a contact.
- Use microcopy to include an explanation of a displayed status on a contact record.
- DO: use text with clear messaging.
- DO: use text formatting thoughtfully. For example, don't bold all of the text that can be seen. Instead, only bold key words and phrases for easier scanning.
- DON'T: use the text component for the primary textual information on a card. Instead, consider using the header component.
- DON'T: use underline formatting for text that's next to a hyperlink, as it will also look clickable.
- DON'T: use microcopy for important or critical information. Instead, consider whether an alert component would fit better.
- DON'T: use text components in place of headers, alerts, and errors.
The Tile
component renders a square tile that can contain other components. Use this component to create groups of related components.
Using the flush
prop, you can remove left and right padding from the tile contents.
flush={false}
(default)flush={true}
- Group a form and its inputs together.
- Group a bulleted text summary and statistics components together.
The ToggleGroup
component renders a list of selectable options, either in radio button or checkbox form.
- Group label: the text that displays above the group of checkboxes.
- Tooltip: on hover, displays additional information about the field.
- Unchecked checkbox: an unselected checkbox.
- Option label: the text that displays next to the checkbox.
- Option description: the text that displays below the option label to describe the option.
By default, the toggle group will render as a vertical list of checkboxes. Using the toggleType
prop, you can set the options to display as checkboxes or radio buttons. You can also use the inline
prop to stack options horizontally.
toggleType="checkboxList"
(default)toggleType="radioButtonList"
inline={true}

- A radio button list to enable salespeople to select one of four sales packages for a new customer.
- A checkbox list to enable customer support reps to select several options of swag to send to a delightful customer.
- DO: use this component when the user has a small selection of items to choose from. For longer lists of options, consider using the select input instead.
- DO: keep label options concise when possible.
- DON'T: use toggle groups to display long lists of options.
Thank you for your feedback, it means a lot to us.