Create CRM cards with projects (BETA)

example-crm-card-button-clickCustomize your CRM by creating CRM cards for contacts, companies, deals, tickets, and custom objects. Custom CRM cards can display information from external systems, organize HubSpot information, and more. You can position cards in the record's middle pane and the sidebar, and you can customize the content using components.

In the context of HubSpot projects, CRM cards are extensions that are defined within private apps, and serverless functions provide the card's functionality.  An app must have crm.objects.contacts.read and crm.objects.contacts.write scopes to create a CRM card.

To view example projects that contain built-out CRM cards, check out HubSpot's example extension library on GitHub.

Set up CRM card files

To get started, you can follow the quickstart guide to create an example CRM card that retrieves and displays data from an external API on button click.

The following files are required for your CRM card to function. These files can have any name, but must use the file types specified below. The image on the right shows which folders these files should be contained in, which you can learn more about in the project file structure overview.

  • crm-card.json: configures the CRM card extension, including which CRM records can display the card. 
  • crm-card.js: contains the code that the serverless function runs.
  • package.json: contains metadata about the serverless function.
  • serverless.json: the serverless function config file.

projects-crm-card-folder-structure

Card configuration


type  string

The extension type. For CRM cards, use crm-card.

data  object

Defines the CRM card and its functionalities. Includes the following fields:

title  string

The name that displays on the card on CRM records.

location  string

Where the card appears on the CRM record.

  • "crm.record.tab": places the card in the middle pane.
  • "crm.record.sidebar": places the card in the sidebar.
fetch  string 

Defines data that gets fetched for the CRM card. Contains targetFunction and objectTypes.

targetFunction  string

Defines an optional target app function to fetch data for your CRM card. This function will be contained in the serverless.json file of the app.functions folder.

objectTypes  array

Defines which object types will display the card on records, along with the HubSpot properties to send to the serverless function.

name  string  

The name of the object. For custom objects, use p_objectName, where objectName matches the name of the custom object (case sensitive).

propertiesToSend  array

The properties to make available to the serverless function. For example, when wanting to display the contact's first name, include "firstname" in the array.

// custom-card.json { "type": "crm-card", "data": { "title": "Example CRM Card", "location":"crm.record.tab" "fetch": { "targetFunction": "crm-card", "objectTypes": [ { "name": "contacts", "propertiesToSend": ["firstname"] } ] } } }

Card functionality

In addition to setting up the card's JSON config file, you'll need to set up the serverless function, including the JavaScript file that provides the card's UI components and functionality.

Primarily, you need to call the sendResponse function which takes in an object with two available properties:

  • sections: an array that contains UI component objects. In the Getting Started template, components are defined first with constants, then later inserted into the sections array. You can also build components within the sections array itself, or use a combination of both.
  • messages: an object that defines a floating success or error banner. For example, a success alert that appears on successful form submission.

Compatible objects

CRM cards can be created for any CRM objects, including standard and HubSpot-defined objects, as well as custom objects. In the CRM card JSON file, you'll define this within the objectTypes array. 

When building a card for custom objects, you'll reference the object with the following format: p_objectName, where objectName matches the name of the custom object (case sensitive). You'll also use this format when referencing custom objects in the crm::propertyList and crm::table components for cards in the middle pane.


// Object types in the CRM card JSON file "objectTypes": [ { "name": "p_Cats", "propertiesToSend": [] } ]

Card location

In the card's JSON file, you can position a card either within the middle pane or right sidebar of a record by setting the location property. To see an example, follow the quickstart guide to create a card that displays in the middle pane.

By default, when creating a middle pane card, it will be added to the Custom tab. However, you can add it to the Overview tab by clicking Customize this tab on a record page.

customize-tab-options-selectIn addition, when customizing a tab, you can create views based on teams to control which users can view which cards.

  • To display a card in the middle pane of the CRM record, set location to "crm.record.tab". By default, the card will be added to the Custom tab, but you can add it to the Overview tab by clicking Customize this tab on a record page.

quickstart-crm-card-middle-pane

  • To display a card in the right sidebar of the CRM record, set location to "crm.record.sidebar".

sidebar-card

// crm-card.json { "type": "crm-card", "data": { "title": "Custom CRM card", "location":"crm.record.tab", "fetch": { "targetFunction": "crm-card", "objectTypes": [ { "name": "contacts", "propertiesToSend": [] } ] } } }

Display CRM property data

You can display property values of the displayed CRM record by including those properties in the CRM card config file and serverless function.

To display property data in standard components: 

  • In the card's JSON file, add the properties to the propertiesToSend array (line 10).
  • Then, in the serverless function (crm-card.js), define a const to retrieve those values from the context argument (line 2).
  • Reference those properties within card components in the serverless function's  sections array (lines 15, 19, 23)
{ "type": "crm-card", "data": { "title": "Custom CRM card", "fetch": { "targetFunction": "crm-card", "objectTypes": [ { "name": "contacts", "propertiesToSend": ["email", "firstname", "lifecyclestage"] } ] } } } exports.main = async (context = {}, sendResponse) => { const {propertiesToSend: { email, firstname, lifecyclestage }} = context; try { sendResponse({ title: "This card retrieves key contact details.", sections: [ { "type": "descriptionList", "direction": "row", "items": [ { "label": "Email", "value": email }, { "label": "First name", "value": firstname }, { "label": "Lifecycle stage", "value": lifecyclestage } ] } ] }); } catch (error) { console.error(error); } };

 

Cards in the middle pane can also use the crm::propertyList and crm::table components to display CRM property data without having to send properties.

To display CRM property data in cards with the crm:: components, you can reference the properties directly as strings.

// crm-card.js exports.main = async (context = {}, sendResponse) => { sendResponse({ sections: [ { "type":"text", "format":"markdown", "text": "A table that displays contact data." }, { type: 'crm::table', objectTypeId: '0-1', properties: ['email', 'hubspot_owner_id', 'firstname', 'lastname'], pageSize: 3 } ], }); };

Troubleshooting

When a CRM card fails to load, an error message will appear in the card on the CRM record.

custom-card-error-message

  • To navigate to the app's CRM card logs, click the link in the error message.
  • On the Logs tab, click the error to view more information, including the type of error, the error message, and full log details.
custom-card-error-message-logs

Learn more about debugging serverless functions.

Components

Below, learn about the components you can include in a CRM card. You can also use the CRM card builder in your HubSpot account to preview components and copy code snippets to use in your JSON file.

To access the CRM card builder, navigate to CRM Development in the main navigation bar, then click CRM card builder in the left side sidebar menu.

Customize a card with components, which you'll include in the sections array of your CRM card JavaScript file (lines 3-17). Standard components can be used by all card types, while other components can only be used by cards in the middle pane.

Standard components include:

In addition, cards in the middle pane can use the following components:

try { sendResponse({ sections: [ { "type": "text", "format": "markdown", "text": "**Markdown**" }, { "type": "button", "text": "Click me", "onClick": { "type": "SERVERLESS_ACTION_HOOK", "serverlessFunction": "crm-card" } }, ], }); } catch (error) { throw new Error(`There was an error fetching the quote': ${error.message}`); }

Below, learn about each type of component and how to include it in a card.

Standard components

Alert

Renders a single alert.

crm-card-alerts

type  string (required)

The component type. For alerts, use alert.

title  string (required)

The bolded title text of the alert.

body  string or object (required)

The explanatory text that appears below the title. To display plain text, set body to a string value. To use markdown syntax, set body to a text component object.

variant  string

The color of the alert. The following variants are available:

  • info (default)
  • success
  • warning
  • error
// example alert components { "type": "alert", "title": "This is the alert title.", "body": { "type": "text", "format":"markdown", "text": "This is the body text of an `info` alert." }, "variant": "info" }

Button

Renders a single button. Learn more about success and error handling and view examples in HubSpot's extension example library on GitHub.

crm-card-buttons

type  string (required)

The component type. For buttons, use button.

text  string (required)

The button's display text.

tooltip  string

The text displayed in a tooltip on hover.

onClick  action 

The button's action type.

disabled  boolean

Set to true to render the button in a disabled state.

variant  string 

The type of button to display. The following variants are available:

  • primary
  • secondary (default)
  • destructive
// example button component { "type": "button", "text": "Primary button", "tooltip": "Hover text.", "variant":"primary", "onClick": { "type": "SERVERLESS_ACTION_HOOK", "serverlessFunction": "functionName" } }

Button row

Renders a row of button components. Learn more about success and error handling and view examples in HubSpot's extension example library on GitHub.

crm-card-button-row-component

type  string (required)

The component type. For button rows, use buttonRow.

buttons  array (required)

An array containing button component objects. The same properties are available for buttons when included in a button row.

// example button row component { "type": "buttonRow", "buttons": [ { "type": "button", "variant": "primary", "text": "Action 1" }, { "type": "button", "text": "Action 2" }, { "type": "button", "text": "Action 3" }, { "type": "button", "text": "Action 4" } ] }

Divider

Renders a divider for spacing out components.

crm-card-divider

type  string (required)

The component type. For dividers, use divider.

distance  string

The space between the divider and the content above and below it. Can be set to any of the following:

  • extra-small
  • small
  • medium (default)
  • large
  • extra-large
  • flush
// example divider component { "type": "text", "text": "Plain text above the divider." }, { "type": "divider", "distance": "small" }, { "type": "text", "text": "Plain text below the divider" }

Description list

Renders pairs of labels and values. 

crm-card-description-list

type  string (required)

The component type. For description lists, use descriptionList.

items  array (required)

An array that contains an object for each label and value pair. To display plain text, set body to a string value. To use markdown syntax, set body to a text component object.
direction  string

The direction that the label and value pairs are displayed. By default, the value is set to column. You can also set the direction to row.
// example description list component { "type": "descriptionList", "items": [ { "label": "Dog name", "value": "Oscar" }, { "label": "Birthday", "value": "2/13/2021" }, { "label": "Favorite toy", "value": { "type": "text", "format": "markdown", "text": "[Snake](https://www.hubspot.com/)" } } ] }

Form

Renders a form that can contain other components, such as input and button fields. Using events, you can then trigger serverless functions based on button click.

To see an example of a simple form, check out HubSpot's extension example library on GitHub.

crm-card-input-field

type  string (required)

The component type. For forms, use form.

content  array (required)

An array containing other components that represent the data to display in the form. For example:

input  

An input component that renders a fillable form field with a default value.

button  

A button component that renders a form submit button. On click, the button will execute the exampleFunction serverless function. Form buttons must use the "SUBMIT" action type to enable form submissions.

// example form component { "type": "form", "content": [ { "type": "input", "name": "example_input", "inputType": "text", "label": "Example input field", "initialValue": "This is the default value for this field." }, { "type": "button", "text": "Submit form", "onClick": { "type": "SUBMIT", "serverlessFunction": "exampleFunction" } } ] }

Heading

Renders large heading text, supporting plain text and markdown.

crm-card-heading-text

type  string (required)

The component type. For headings, use heading.

text  string (required)

Text to be displayed. Supports markdown when an additional property of "format": "markdown" is included.

// example heading component { "type": "heading", "text": "Heading text" }, { "type": "heading", "format": "markdown", "text": "*Markdown heading* [text](https://www.hubspot.com)" }

Input

Renders an input field. Can only be used within form components.

crm-card-input-fields

type  string (required)

The component type. For inputs, use input.

name  string (required)

The input's unique identifier, similar to the HTML input element name attribute.

label  string 

The text that displays above the input. Required if inputType is not set to hidden.

inputType  string

The type of form input to display. Supports the following values:

  • text (default): displays a standard single-line text input field.
  • hidden: the input is not displayed but contains a value that gets passed on form submission.
initialValue  string

The value that the field is initially set to on load.

pattern  string (required)

Defines a regex pattern for validation.

validationErrorMessage  string 

The error message to display when the regex pattern defined in pattern isn't matched. This field is required when including pattern.

readonly  boolean 

Sets the field as read-only on the CRM record. Users will not be able to fill the input field when set to true.

required  boolean 

Sets the input as required for the form to be submittable.

requiredValidationMessage  string 

The error message to display when the user hasn't entered a value in a required field.

// example input components { "type": "input", "name": "magicId", "inputType": "hidden", "label": "ID value that references another system", "initialValue": "589fe9fa-a691-4a88-a073-ca8d70d3bc9b" }, { "type": "input", "name": "one", "inputType": "text", "readonly": true, "label": "Input 1" }, { "type": "input", "name": "two", "inputType": "text", "label": "Input 2", "initialValue": "default value", "pattern": "[a-zA-Z0-9 ]{4,10}", "validationErrorMessage": "Value must contain only letters and numbers and be between a 4-10 characters" }

Image

Renders an image.

crm-card-image

type  string (required)

The component type. For images, use image.

src  string (required)

The URL of the image.

alt  string (required)

The image's alt text.

width  number 

Sets a max-width for the image. When resizing, HubSpot will automatically adjust the height to respect the image's original aspect ratio.

Images cannot exceed the width of the CRM card at various screen sizes, and values beyond that maximum width will not be applied to the image.

onClick  object

A card action object that triggers an action on element click.

// example image component { "type":"image", "src":"https://hubspot.com/hubfs/the-sprocket.svg", "alt":"HubSpot logo", "width":100, "onClick": { "type": "SERVERLESS_ACTION_HOOK", "serverlessFunction": "my-custom-function" } }

Stack

Renders a vertical stack of other components with adjustable spacing.

crm-card-component-stack

 

type  string (required)

The component type. For stacks, use stack.

content  array (required)

The components to render in the stack.

distance  string

The amount of vertical space between each component in the content array. Can be set to "small" (default) or "flush".

// example stack component { "type": "stack", "distance": "flush", "content": [ { "type": "heading", "text": "Stack example" }, { "type": "text", "format": "markdown", "text": "`stack` can be used to alter the vertical spacing between components." } ] }

Statistics

Renders data summaries.

crm-card-statistics

type  string (required)

The component type. For statistics, use statistic.

items  array (required)

An array of item objects representing each piece of data to display. Each item object can contain the following properties:

  • label
  • number
  • description

Learn more about each property below.

label string (required) 

The statistic's text label.

number string (required) 

The number that displays under the label.

description string or object

Displays helper information under the statistic.

  • To display the description as plain text, enter your text as a string.
  • To instead use markdown syntax, set description to a text component object with "format": "markdown".
  • To display the description as a trend with a directional indicator, set description to an object with the following properties:
    • type: trend
    • value: the statistic value to display.
    • direction: displays an arrow indicating whether the data is trending upwards or downwards. Accepts either increase or decrease.
// example statistics component { "type": "statistics", "items": [ { "label": "Views last week", "number": "55", "description": "Apr 11 - Apr 17" }, { "label": "Views this month", "number": "203", "description": { "type": "trend", "value": "23.36%", "direction": "increase" } }, { "label": "Markdown syntax", "number": "405", "description": { "type": "text", "format": "markdown", "text": "[Feb 12 - Feb 19](https://app.hubspot.com/)" } } ] }

Tag

Renders a tag.

crm-card-tags

type  string (required)

The component type. For tags, use tag.

text  string (required)

The tag's label.

variant  string

The tag's color. The following variants are available: default, warning, success, and error.

onClick  object

A card action object that triggers an action on element click.

// example tag component { "type": "tag", "text": "Tag text", "variant": "default", "onClick": { "type": "SERVERLESS_ACTION_HOOK", "serverlessFunction": "my-custom-function" } }

Text

Renders text, supporting either plain text or markdown.

crm-card-text

type  string (required)

The component type. For text, use text.

format  string

Optionally, you can include format with a value of markdown to render the text with markdown syntax. Learn more about supported syntax below.
text string (required)

The text to display. If format is set to markdown, this property supports the following markdown syntax:

  • Bold:
    • **example**
    • __example__
  • Italics:
    • *example*
    • _example_
  • Inline code: `example`
  • Links: [visible anchor text](https://www.hubspot.com)
variant string

The style of text to display. Can be either of the following values for either plaintext or markdown:

  • bodytext: the default value which renders the standard text size.
  • microcopy: smaller text used for adding context.
// example markdown component { "type": "text", "text": "This is a plaintext string" }, { "type": "text", "format": "markdown", "text": "The word **bold** is bolded, and the word [link](https://www.hubspot.com/) is a link. here's `code` and _italics_ too" }

Tile

Renders a square tile containing other components.

crm-card-tile

type  string (required)

The component type. For tiles, use tile.

body  array (required)

An array containing other CRM card component objects.

// example tile component { "type": "tile", "body": [ { "type": "text", "text": "Some Important content" }, { "type": "text", "format": "markdown", "text": "some **very** _related_ content with a [link](https://www.hubspot.com/)" } ] }

Middle pane card components

Property list

Built on top of the description list component, this component enables you to display a read-only list of property values from a specified CRM record. This component enables you to access property data from specific records without needing to configure propertiesToSend.

Available for cards in the middle pane only.

Please note: record IDs are unique to each account. If you're developing in multiple accounts, including sandbox accounts, you'll need to update the component to use IDs that are specific to each account. 

crm-propertylist-component

type  string (required)

The component type. For this component, use crm::propertyList.

objectTypeId  string (required)

The type of object you're pulling data from (i.e., contacts for contacts).

When referencing a custom object, use the format p_objectName where objectName matches the custom object's name value exactly.

objectId  string (required)

The ID of the specified CRM record to display properties from.

properties  array (required)

The internal names of the properties to display.

direction  string

The direction that the properties will display in. By default, direction is set to "column". Set to "row" to display properties in rows.

// example crm::propertyList component { "type": "crm::propertyList", "objectTypeId": "contacts", "objectId": "9451", "properties": ["firstname", "lastname", "company"] }

Table

Displays a table of CRM data for a specified object, including custom objects. Specified properties will display as columns, with records filling out rows. This component enables you to access property data from records without needing to configure propertiesToSend.

Available for cards in the middle pane only.

crm-table-component

type  string (required)

The component type. For this component, use crm::table.

objectTypeId  string (required)

The type of object you're pulling data from (i.e., contacts for contacts).

When referencing a custom object, use the format p_objectName where objectName matches the custom object's name value exactly.

properties  array (required)

Contains the properties to display as table columns.

pageSize  string

Maximum number of rows to shower per page. Defaults to 10.

// example crm::table component { "type": "crm::table", "objectTypeId": "contacts", "properties": [ "email", "hubspot_owner_id", "firstname", "lastname" ], "pageSize": 3 }

Card actions

To enable your CRM card to interact with data, either externally or within HubSpot, you can add actions through components, such as a button or image, with the onClick property.

crm-card-primary-button

"sections": [ { "type": "text", "text": "Click the button below to retrieve data." }, { "type": "button", "text": "Primary button", "tooltip": "Hover text.", "variant":"primary", "onClick": { "type": "SERVERLESS_ACTION_HOOK", "serverlessFunction": "crm-card" } } ]

In the CRM card builder, you can view the payload of different actions types by using the Action details tab in the card builder:

  • In the CRM card builder, click the button component to add it to the middle pane.
  • In the right pane, set the type of action. For example, ACTION_HOOK.
  • In the middle pane, click the button to trigger the event.
  • In the right pane, click the Action details tab to view the action output.

crm-card-builder-action-details

Below, learning more about the available types of actions.

Action hooks

Send a server-side request to an app to display a success or error message. This type of action is useful for simple operations that don’t require further user input, but requires you to run your own server to handle requests. 

type  string (required)

The type of action. For action hooks, use ACTION_HOOK.

httpMethod  string (required)

The HTTP method to use when making the request

uri  string (required)

The URI of the request.

associatedObjectProperties  array 

A list of properties on the displayed CRM record. If httpMethod is GET or DELETE, these properties will be appended to the request uri as query parameters. Otherwise, they'll be sent as a JSON request body.

// Button with iframe action type { "type": "button", "text": "Submit Form", "onClick": { "type": "ACTION_HOOK", "httpMethod": "POST", "uri": "https://example.com/action-hook", "associatedObjectProperties": ["some_crm_property"] } }

Serverless action hooks

Call a serverless function included in the project. You'll reference the name of the function within the targetFunction field of the card's JSON config file. The name of the function should match the function's name within the serverless.json file as well as the function that gets called in the JavaScript file. 

To see an example of a serverless action hook, use the quickstart guide to download and view the Getting Started project template files.

type  string (required)

The type of action. For serverless action hooks, use SERVERLESS_ACTION_HOOK.

serverlessFunction  string (required)

The name of the serverless function as declared in the project's serverless.json file.

associatedObjectProperties  array 

A list of properties on the displayed CRM record. These property values will be included in the serverless function's context.

// Button with serverless action hook { "type": "button", "text": "Submit Form", "onClick": { "type": "SERVERLESS_ACTION_HOOK", "serverlessFunction": "my-custom-function", "associatedObjectProperties": ["additional_crm_property"] } }

iframe

Open a modal dialog within an iframe to display content.

To see an example of using an iframe, check out HubSpot's extension example library on GitHub.

type  string(required)

The type of action. For iframe hooks, use IFRAME.

height  number (required)

The height of the frame.

width  number (required)

The width of the frame.

uri  string (required)

The URI of the content.

associatedObjectProperties  array

A list of properties on the displayed CRM record. Property values will be appended to the iframe uri as query parameters.

// Button with iframe action type { "type": "button", "text": "Submit Form", "onClick": { "type": "IFRAME", "width": 890, "height": 748, "uri": "https://example.com/iframe-contents", "associatedObjectProperties": ["additional_crm_property"] } }

Submit

Submits a form to the specified serverless function with a form's current state. This action type should only be used with forms. To see an example of a simple form with a submit action, check out HubSpot's extension example library on GitHub.

type  string (required)

The type of action. For form submissions, use SUBMIT.

serverlessFunction  string (required)

The name of the serverless function to invoke, as declared in the project's serverless.json file.

// Button with Submit action type { "type": "button", "text": "Submit Form", "onClick": { "type": "SUBMIT", "serverlessFunction": "exampleFunction" } }

Events

Events are added to the serverless context when a user's action triggers a serverless function. Events will be included in the serverless functions context under event if the serverless function was triggered by a specific user event, similar to web events.

To see an example of a project that uses a form and event, check out HubSpot's extension example library on GitHub.

To use events, you'll first set up a for submit type action within a button component that triggers a serverless function:

// Button with Submit action type { "type": "button", "text": "Submit Form", "onClick": { "type": "SUBMIT", "serverlessFunction": "exampleFunction" } }

Then, in the serverless function you can reference the event within the context argument. The example below checks for a SUBMIT event, then creates a task in Asana using the values in the submitted form:

try { const client = asana.Client.create().useAccessToken(process.env.ASANA_PAT); if (context.event && context.event.type === "SUBMIT") { await client.tasks.createTask({ name: context.event.payload.formState.taskName, notes: context.event.payload.formState.taskNotes, projects: [`${process.env.ASANA_PROJECT}`], pretty: true, }); } sendResponse({ sections, message: { type: "SUCCESS", body: "Task added to Asana!", }, }); } catch (error) { sendResponse({ message: { type: "ERROR", body: `Error: ${error.message}`, }, }); }

Success and error handling

If your CRM card includes an action that's triggered by user input, such as a button, you can display a notification to indicate whether the action succeeded or failed. After the action completes, you can trigger the notification by providing a message in a sendResponse call.

To see examples of success and error banners, check out HubSpot's extension example library on GitHub.

type  string (required)

The type of notification banner. Use 'SUCCESS' for a green banner and 'ERROR' for a red banner.

body  string (required)

The message displayed within the notification banner.

sendResponse({ message: { type: 'SUCCESS', body: 'Successfully executed action!' } });

crm-card-success-notification

For example, if your CRM card had a button that triggers an API call, you could specify the success and error handling within a separate serverless function (e.g., handle-response.js), then reference that serverless function within the original CRM card JavaScript file (e.g., crm-card.js)

const hubspot = require('@hubspot/api-client'); exports.main = async (context = {}, sendResponse) => { // Instantiating HubSpot node API client const hubspotClient = new hubspot.Client({ accessToken: context.secrets.PRIVATE_APP_ACCESS_TOKEN, }); try { await hubspotClient.crm.contacts.basicApi.getPage() .then((data) => { sendResponse({ message: { type: "SUCCESS", body: `Successfully executed action!` } }) }) .catch((error) => { sendResponse({ message: { type: 'ERROR', body: `There was an error: ${error}` } }) }); } catch (error) { throw new Error( `There was an error executing the serverless function: ${error.message}` ); } };exports.main = async (context = {}, sendResponse) => { try { sendResponse({ sections: [ { "type": "text", "text": "Trigger action" }, { "type": "button", "text": "Action with message", "variant": "primary", "onClick": { "type": "SERVERLESS_ACTION_HOOK", "serverlessFunction": "handle-response" } }, ] }); } catch (error) { throw new Error( `There was an error creating these cards': ${error.message}` ); } };


Was this article helpful?
This form is used for documentation feedback only. Learn how to get help with HubSpot.