Skip to content

Instantly share code, notes, and snippets.

@dazld
Created July 3, 2025 20:32
Show Gist options
  • Save dazld/1bc9f288b7e997ad64d743cf9d68319d to your computer and use it in GitHub Desktop.
Save dazld/1bc9f288b7e997ad64d743cf9d68319d to your computer and use it in GitHub Desktop.

HubSpot UI Extensions API Documentation

Overview

This documentation covers all components, APIs, and patterns used across the HubSpot UI Extensions examples repository. The examples demonstrate how to build custom UI extensions for HubSpot CRM using React/TypeScript.

Core Architecture

Extension Entry Point

All extensions follow the same initialization pattern:

import { hubspot } from '@hubspot/ui-extensions';

// Basic extension
hubspot.extend(() => <Component />);

// Extension with actions
hubspot.extend(({ actions }) => <Component actions={actions} />);

// Extension with context and actions
hubspot.extend<'crm.record.tab'>(({ context, actions }) => 
  <Component context={context} actions={actions} />
);

TypeScript Integration

Extensions use generic type parameters to specify their placement:

  • hubspot.extend<'crm.record.tab'> - CRM record tab
  • Context and actions are strongly typed with interfaces from @hubspot/ui-extensions

Core Actions and Hooks

CRM Actions

fetchCrmObjectProperties(properties: string[])

Retrieves CRM object properties.

Parameters:

  • properties: Array of property names to fetch

Returns: Promise with property values

Example:

const properties = await actions.fetchCrmObjectProperties([
  'firstname', 'lastname', 'email'
]);

onCrmPropertiesUpdate(properties: string[], callback)

Listens for property changes.

Parameters:

  • properties: Array of property names to watch
  • callback: Function called when properties change

Example:

actions.onCrmPropertiesUpdate(['lifecyclestage'], (updatedProperties) => {
  setProperties(updatedProperties);
});

refreshObjectProperties()

Refreshes properties displayed on the page.

Example:

await actions.refreshObjectProperties();

addAlert({ type, message })

Shows alerts to users.

Parameters:

  • type: 'success' | 'warning' | 'danger' | 'info'
  • message: Alert message string

Example:

actions.addAlert({
  type: 'success',
  message: 'Property updated successfully'
});

closeOverlay(id: string)

Closes modals/panels.

Parameters:

  • id: Overlay identifier

Example:

actions.closeOverlay('modal-id');

Serverless Integration

hubspot.serverless(functionName, options)

Calls serverless functions.

Parameters:

  • functionName: Name of the serverless function
  • options: Configuration object

Options:

  • propertiesToSend: Array of CRM properties to send
  • parameters: Object with function parameters
  • payload: Request payload

Example:

const result = await hubspot.serverless('updateDeal', {
  propertiesToSend: ['amount', 'dealstage'],
  parameters: { dealId: '123' },
  payload: { newAmount: 50000 }
});

External API Integration

hubspot.fetch(url, options)

Makes HTTP requests to external APIs.

Parameters:

  • url: API endpoint URL
  • options: Fetch options object

Example:

const response = await hubspot.fetch('https://api.example.com/data', {
  method: 'GET',
  headers: { 'Authorization': 'Bearer token' }
});

Components by Category

Layout Components

Flex

Flexible layout container.

Props:

  • direction: 'row' | 'column' | 'row-reverse' | 'column-reverse'
  • gap: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'flush'
  • align: 'start' | 'center' | 'end' | 'stretch'
  • justify: 'start' | 'center' | 'end' | 'between' | 'around'
  • wrap: 'nowrap' | 'wrap' | 'wrap-reverse'

Example:

<Flex direction="column" gap="md" align="center">
  <Text>Item 1</Text>
  <Text>Item 2</Text>
</Flex>

Box

Basic container with flex properties.

Props:

  • flex: Number (flex-grow ratio)

Example:

<Flex direction="row">
  <Box flex={1}>Left content</Box>
  <Box flex={2}>Right content (wider)</Box>
</Flex>

Tile

Basic container component for grouping content.

Example:

<Tile>
  <Heading>Title</Heading>
  <Text>Content</Text>
</Tile>

Form Components

Form

Form container with submission handling.

Props:

  • onSubmit: Function called on form submission
  • preventDefault: Boolean to prevent default form submission

Example:

<Form onSubmit={handleSubmit} preventDefault={true}>
  <Input name="email" label="Email" required />
  <Button type="submit">Submit</Button>
</Form>

Input

Text input field.

Props:

  • name: Input name attribute
  • label: Input label
  • required: Boolean for required validation
  • placeholder: Placeholder text
  • description: Help text below input
  • tooltip: Tooltip text
  • value: Controlled value
  • onChange: Change handler

Example:

<Input 
  name="firstname"
  label="First Name"
  required
  placeholder="Enter first name"
  description="Your legal first name"
  tooltip="This will appear on official documents"
  value={firstName}
  onChange={setFirstName}
/>

TextArea

Multi-line text input.

Props:

  • name: Input name attribute
  • label: Input label
  • maxLength: Maximum character limit
  • placeholder: Placeholder text
  • value: Controlled value
  • onChange: Change handler

Example:

<TextArea 
  name="description"
  label="Description"
  maxLength={500}
  placeholder="Enter description"
  value={description}
  onChange={setDescription}
/>

Select

Dropdown selection component.

Props:

  • options: Array of option objects
  • value: Selected value
  • onChange: Change handler
  • label: Select label
  • placeholder: Placeholder text

Option Object:

  • label: Display text
  • value: Option value

Example:

<Select 
  label="Status"
  options={[
    { label: 'Active', value: 'active' },
    { label: 'Inactive', value: 'inactive' }
  ]}
  value={status}
  onChange={setStatus}
/>

DateInput

Date selection input.

Props:

  • format: Date format string
  • value: Date value
  • onChange: Change handler
  • label: Input label

Example:

<DateInput 
  label="Start Date"
  format="MM/DD/YYYY"
  value={startDate}
  onChange={setStartDate}
/>

NumberInput

Numeric input with validation.

Props:

  • min: Minimum value
  • max: Maximum value
  • value: Numeric value
  • onChange: Change handler
  • label: Input label

Example:

<NumberInput 
  label="Price"
  min={0}
  max={10000}
  value={price}
  onChange={setPrice}
/>

Display Components

Text

Text display component.

Props:

  • variant: 'default' | 'microcopy'
  • format: 'currency' | 'date' | 'datetime' | 'number' | 'time'
  • inline: Boolean for inline display

Example:

<Text variant="microcopy" format="currency">
  {price}
</Text>

Heading

Heading text component.

Example:

<Heading>Section Title</Heading>

StatusTag

Status indicator component.

Props:

  • variant: 'success' | 'warning' | 'error' | 'info'

Example:

<StatusTag variant="success">Active</StatusTag>

Statistics & StatisticsItem

Metrics display components.

Example:

<Statistics>
  <StatisticsItem label="Total Sales" value="$50,000" />
  <StatisticsItem label="New Leads" value="25" />
</Statistics>

Alert

Alert message component.

Props:

  • title: Alert title
  • variant: 'success' | 'warning' | 'danger' | 'info'
  • children: Alert content

Example:

<Alert title="Success" variant="success">
  Operation completed successfully
</Alert>

Interactive Components

Button

Interactive button component.

Props:

  • variant: 'primary' | 'secondary' | 'destructive'
  • size: 'xs' | 'sm' | 'md' | 'lg'
  • type: 'button' | 'submit' | 'reset'
  • disabled: Boolean for disabled state
  • overlay: Overlay configuration object
  • onClick: Click handler

Overlay Configuration:

  • type: 'modal' | 'panel'
  • title: Overlay title
  • body: Overlay content
  • width: Overlay width
  • height: Overlay height

Example:

<Button 
  variant="primary" 
  size="md"
  onClick={handleClick}
  overlay={{
    type: 'modal',
    title: 'Confirmation',
    body: <ConfirmationForm />,
    width: 'md'
  }}
>
  Open Modal
</Button>

Link

External link component.

Props:

  • href: URL destination

Example:

<Link href="https://example.com">Visit Website</Link>

Data Components

Table

Data table component.

Props:

  • bordered: Boolean for table borders
  • paginated: Boolean for pagination
  • pageCount: Number of pages
  • showButtonLabels: Boolean for pagination button labels

Example:

<Table bordered paginated pageCount={5}>
  <TableHead>
    <TableRow>
      <TableCell>Name</TableCell>
      <TableCell>Email</TableCell>
    </TableRow>
  </TableHead>
  <TableBody>
    {data.map(item => (
      <TableRow key={item.id}>
        <TableCell>{item.name}</TableCell>
        <TableCell>{item.email}</TableCell>
      </TableRow>
    ))}
  </TableBody>
</Table>

CrmAssociationTable

CRM-specific association table.

Props:

  • objectTypeId: CRM object type ID
  • propertyColumns: Array of property column definitions
  • preFilters: Array of pre-applied filters
  • sort: Sort configuration
  • pagination: Pagination configuration

Property Column Definition:

  • propertyName: CRM property name
  • label: Column header label
  • sortable: Boolean for sortable column

Example:

<CrmAssociationTable 
  objectTypeId="0-1"
  propertyColumns={[
    { propertyName: 'name', label: 'Company Name', sortable: true },
    { propertyName: 'industry', label: 'Industry', sortable: false }
  ]}
  sort={{ propertyName: 'name', direction: 'ASC' }}
  pagination={{ pageSize: 10 }}
/>

CrmStageTracker

CRM deal stage tracking component.

Props:

  • properties: Array of CRM properties to display
  • showProperties: Boolean to show/hide properties

Example:

<CrmStageTracker 
  properties={['amount', 'closedate', 'dealstage']}
  showProperties={true}
/>

Chart Components

BarChart

Bar chart visualization.

Props:

  • data: Array of data objects
  • axes: Axis configuration
  • options: Chart options

Example:

<BarChart 
  data={salesData}
  axes={{
    x: { property: 'month' },
    y: { property: 'sales' }
  }}
  options={{
    title: 'Monthly Sales',
    legend: { show: true }
  }}
/>

LineChart

Line chart visualization.

Props:

  • data: Array of data objects
  • axes: Axis configuration
  • options: Chart options

Example:

<LineChart 
  data={trendData}
  axes={{
    x: { property: 'date' },
    y: { property: 'value' }
  }}
  options={{
    title: 'Sales Trend',
    smooth: true
  }}
/>

Modal and Panel Components

Modal

Modal overlay component.

Props:

  • title: Modal title
  • onClose: Close handler
  • size: 'sm' | 'md' | 'lg' | 'xl'

Example:

<Modal title="Edit Record" onClose={handleClose} size="md">
  <ModalBody>
    <Form>
      <Input name="name" label="Name" />
    </Form>
  </ModalBody>
  <ModalFooter>
    <Button variant="secondary" onClick={handleClose}>Cancel</Button>
    <Button variant="primary" onClick={handleSave}>Save</Button>
  </ModalFooter>
</Modal>

ModalBody

Modal content area.

Example:

<ModalBody>
  <Text>Modal content goes here</Text>
</ModalBody>

ModalFooter

Modal footer with actions.

Example:

<ModalFooter>
  <Button variant="secondary">Cancel</Button>
  <Button variant="primary">Confirm</Button>
</ModalFooter>

Panel

Side panel component.

Props:

  • title: Panel title
  • onClose: Close handler
  • variant: 'default' | 'flush'

Example:

<Panel title="Details" onClose={handleClose}>
  <PanelBody>
    <Text>Panel content</Text>
  </PanelBody>
  <PanelFooter>
    <Button onClick={handleClose}>Close</Button>
  </PanelFooter>
</Panel>

Utility Components

LoadingSpinner

Loading indicator component.

Example:

{loading && <LoadingSpinner />}

ErrorState

Error state display component.

Props:

  • title: Error title
  • message: Error message

Example:

<ErrorState 
  title="Failed to Load"
  message="Unable to fetch data. Please try again."
/>

EmptyState

Empty state display component.

Props:

  • title: Empty state title
  • message: Empty state message

Example:

<EmptyState 
  title="No Data"
  message="No records found matching your criteria."
/>

StepIndicator

Multi-step process indicator.

Props:

  • currentStep: Current step number
  • totalSteps: Total number of steps
  • steps: Array of step objects

Step Object:

  • title: Step title
  • completed: Boolean for completion status

Example:

<StepIndicator 
  currentStep={2}
  totalSteps={3}
  steps={[
    { title: 'Details', completed: true },
    { title: 'Review', completed: false },
    { title: 'Confirm', completed: false }
  ]}
/>

DescriptionList

Key-value pair display component.

Example:

<DescriptionList>
  <DescriptionListItem label="Name" value="John Doe" />
  <DescriptionListItem label="Email" value="[email protected]" />
</DescriptionList>

TypeScript Interfaces

Common Action Interfaces

interface ExtensionProps {
  actions: {
    fetchCrmObjectProperties: FetchCrmObjectPropertiesAction;
    onCrmPropertiesUpdate: OnCrmPropertiesUpdateAction;
    refreshObjectProperties: RefreshObjectPropertiesAction;
    addAlert: AddAlertAction;
    closeOverlay: (id: string) => void;
  };
  context: CrmContext;
}

interface FetchCrmObjectPropertiesAction {
  (properties: string[]): Promise<Record<string, any>>;
}

interface OnCrmPropertiesUpdateAction {
  (properties: string[], callback: (updatedProperties: Record<string, any>) => void): void;
}

interface AddAlertAction {
  (alert: { type: 'success' | 'warning' | 'danger' | 'info'; message: string }): void;
}

Common Data Interfaces

interface MenuItem {
  id: number;
  name: string;
  price: number;
  description: string;
  category: string;
  available: boolean;
}

interface Company {
  id: string;
  name: string;
  industry: string;
  city: string;
  state: string;
  distance?: number;
}

interface PropertyData {
  name: string;
  value: any;
  type: string;
  label: string;
}

Enum Types

enum PropertyType {
  Bedroom = 'bedroom',
  Studio = 'studio',
  BedroomX2 = 'bedroom_x2',
}

enum ListingItemStatus {
  Available = 'success',
  InProgress = 'warning',
  Occupied = 'error',
}

enum FlexDirection {
  Row = 'row',
  Column = 'column',
  RowReverse = 'row-reverse',
  ColumnReverse = 'column-reverse',
}

State Management Patterns

React Hooks Usage

// Basic state management
const [data, setData] = useState<DataType[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string>('');

// Effect for data fetching
useEffect(() => {
  const fetchData = async () => {
    setLoading(true);
    try {
      const result = await hubspot.fetch('/api/data');
      setData(result);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };
  
  fetchData();
}, []);

// Memoized callback
const handleAction = useCallback((item: DataType) => {
  // Handle action
}, [dependencies]);

Form State Management

interface FormData {
  name: string;
  email: string;
  message: string;
}

const [formData, setFormData] = useState<FormData>({
  name: '',
  email: '',
  message: ''
});

const [errors, setErrors] = useState<Partial<FormData>>({});

const handleInputChange = (field: keyof FormData, value: string) => {
  setFormData(prev => ({ ...prev, [field]: value }));
  
  // Clear error for this field
  if (errors[field]) {
    setErrors(prev => ({ ...prev, [field]: undefined }));
  }
};

const validateForm = (): boolean => {
  const newErrors: Partial<FormData> = {};
  
  if (!formData.name) newErrors.name = 'Name is required';
  if (!formData.email) newErrors.email = 'Email is required';
  
  setErrors(newErrors);
  return Object.keys(newErrors).length === 0;
};

Best Practices

Error Handling

// Centralized error handling
const handleAsyncOperation = async () => {
  try {
    setLoading(true);
    const result = await someAsyncOperation();
    setData(result);
    
    actions.addAlert({
      type: 'success',
      message: 'Operation completed successfully'
    });
  } catch (error) {
    console.error('Operation failed:', error);
    
    actions.addAlert({
      type: 'danger',
      message: 'Operation failed. Please try again.'
    });
  } finally {
    setLoading(false);
  }
};

Loading States

// Show loading states during async operations
{loading ? (
  <LoadingSpinner />
) : error ? (
  <ErrorState title="Error" message={error} />
) : data.length === 0 ? (
  <EmptyState title="No Data" message="No records found" />
) : (
  <DataDisplay data={data} />
)}

Component Composition

// Reusable component patterns
const PropertyCard = ({ property }: { property: PropertyData }) => (
  <Tile>
    <Flex direction="column" gap="sm">
      <Heading>{property.label}</Heading>
      <Text format={property.type}>{property.value}</Text>
    </Flex>
  </Tile>
);

// Compose multiple components
const PropertyList = ({ properties }: { properties: PropertyData[] }) => (
  <Flex direction="column" gap="md">
    {properties.map(property => (
      <PropertyCard key={property.name} property={property} />
    ))}
  </Flex>
);

Type Safety

// Use strong typing for props
interface ComponentProps {
  data: DataType[];
  onSelect: (item: DataType) => void;
  loading?: boolean;
  error?: string;
}

// Type guard functions
const isValidData = (data: any): data is DataType => {
  return data && typeof data.id === 'string' && typeof data.name === 'string';
};

// Generic component types
interface GenericListProps<T> {
  items: T[];
  renderItem: (item: T) => React.ReactNode;
  keyExtractor: (item: T) => string;
}

This comprehensive API documentation covers all the components, patterns, and best practices found across the HubSpot UI Extensions examples repository. Each component includes detailed prop definitions, usage examples, and integration patterns with the HubSpot platform.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment