As you build out your UI extensions, you can leverage npm workspaces to share code across your app cards, settings page, and app home page. This allows you to consolidate common logic such as formatting dates and currency, and ensure that any fixes or updates are rolled out consistently to each UI extension.
Prerequisites
Before getting started, install the latest version of the HubSpot CLI. In a terminal window, run the following command:
npm install -g @hubspot/cli
The CLI needs to be version 8.0.0 or later.
- You can check which version of the CLI you have by running
hs --version.
- If needed, you can run the command
npm install -g @hubspot/cli@latest to update to the latest version of the HubSpot CLI.
Project structure
The code for your card, setting page, and home page follows the same directory structure as outlined in the app configuration article. The main change you’ll need to make to set up code sharing between your extensions will be to add a package.json file at the root of your project under src/app/. The naming convention and scope you choose for these shared packages is entirely customizable, allowing you to use a pattern you’re already comfortable with, such as @packages/types.
An example directory structure that demonstrates the root package.json location, along with the shared code for types, utils, and components is shown below:
my-hubspot-project/
└── hsproject.json
└── src/
└── app/
└── app-hsmeta.json
└── package.json
└── packages/
└── types/
└── package.json
└── index.ts
└── utils/
└── package.json
└── index.ts
└── components/
└── package.json
└── index.tsx
└── cards/
└── MyCard.jsx
└── my-app-card-hsmeta.json
└── package.json
└── settings/
└── Settings.tsx
└── settings-hsmeta.json
└── package.json
└── pages/
└── Home.tsx
└── package.json
After you create a package.json file in the src/app/ directory of your project, open the package.json file and define the name and workspaces fields:
name: the name of your project (e.g., my-hubspot-project).
workspaces: an array that includes the UI extension directories where you plan to use your shared code, along with the directory where your shared code lives (e.g., packages/*).
For example, following the same directory structure shown above, the resulting package.json file would be:
{
"name": "my-hubspot-project",
"workspaces": ["cards", "settings", "pages", "packages/*"]
}
Create shared packages
You can use any naming convention when defining the package scope for your shared code (e.g., @my-hubspot-project in the examples below).
The code blocks below provide examples for defining shared types, utils, and components that you can then import into your UI extensions.
Types
The examples below define a simple Money type under packages/types in your project.
{
"name": "@my-hubspot-project/types",
"private": true,
"version": "1.0.0",
"main": "index.ts"
}
export interface Money {
amount: number;
currency: 'USD' | 'EUR' | 'GBP';
}
Utilities
The examples below define a utility under packages/utils that will format data using the Money type from above.
{
"name": "@my-hubspot-project/utils",
"private": true,
"version": "1.0.0",
"main": "index.ts",
"dependencies": {
"@my-hubspot-project/types": "1.0.0"
}
}
import { Money } from "@my-hubspot-project/types";
export function formatDate(date: Date | string): string {
const d = typeof date === 'string' ? new Date(date) : date;
return d.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
});
}
export function formatMoney(money: Money): string {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: money.currency,
}).format(money.amount);
}
Components
The examples below demonstrate a shared component that lives under packages/components.
{
"name": "@my-hubspot-project/components",
"private": true,
"version": "1.0.0",
"main": "index.tsx",
"dependencies": {
"@hubspot/ui-extensions": "latest",
"react": "^18.2.0"
}
}
import { Text, Flex, Divider } from "@hubspot/ui-extensions";
interface SectionProps {
title: string;
children: React.ReactNode;
}
export const Section = ({ title, children }: SectionProps) => (
<Flex direction="column" gap="sm">
<Text format={{ fontWeight: "bold" }}>{title}</Text>
<Divider />
{children}
</Flex>
);
Add dependencies to extensions
After you’ve defined your shared code under packages/, add the path to your shared packages in each of your extension’s respective package.json files:
{
"name": "@my-hubspot-project/my-cards",
"dependencies": {
"@hubspot/ui-extensions": "latest",
"@my-hubspot-project/types": "1.0.0",
"@my-hubspot-project/components": "1.0.0",
"@my-hubspot-project/utils": "1.0.0"
}
}
Use shared code in your UI extensions
The following code block shows how to use your shared code within one of your UI extensions:
import { hubspot } from "@hubspot/ui-extensions";
import { Text } from "@hubspot/ui-extensions";
import { Money } from "@my-hubspot-project/types";
import { Section } from "@my-hubspot-project/components";
import { formatDate, formatMoney } from "@my-hubspot-project/utils";
const dealAmount: Money = { amount: 15000, currency: 'USD' };
hubspot.extend<'crm.record.tab'>(({ context }) => (
<Section title="Deal Summary">
<Text>Amount: {formatMoney(dealAmount)}</Text>
<Text>Close Date: {formatDate('2024-03-15')}</Text>
</Section>
));
After you’ve added the imports of your shared code in your UI extensions, you can proceed with the next step of installing the shared packages so the project will build.
Install shared packages
To ensure your shared packages can be imported successfully in your UI extension, run the following command anywhere in your project:
Alternatively, your shared packages will automatically be installed next time you run hs project dev or hs project upload. Last modified on February 12, 2026