An interactive showcase app for hs-uix — a family of higher-level components for the HubSpot UI Extensions platform. The app installs as a private HubSpot app and renders a browsable gallery of live, customizable demos: each one runs the real component, links to its source, and lets you copy the example code straight to your clipboard.
Built on the HubSpot UI Extensions (@hubspot/ui-extensions) framework and deployed with the HubSpot CLI (hs).
The app's home page (/app/pages/Home.jsx) renders a DemoBrowser with one tab per package. Every view shares one layout: a demo rendered inline with the rest of its package's demos in a tile grid below. Tabs lead with their flagship demo; clicking any tile swaps the selected demo into that tab's inline slot (with a back button), so the tab bar always stays visible.
| Tab | Component(s) | What it demonstrates |
|---|---|---|
| DataTable | DataTable |
Search, filters, sorting, grouping, footer totals, inline & row editing, selection, bulk actions |
| FormBuilder | FormBuilder |
Declarative form schemas, validation, field types, layout |
| Kanban | Kanban |
Draggable columns/cards and board state |
| CRM Search | CRM search components | Querying and rendering CRM object results |
| Feed | Feed |
Activity/feed layouts and item variants |
| Calendar | Calendar |
Calendar views and date interactions |
| Common Components | shared building blocks + icon set | Reusable primitives used across the library, plus the icon catalog |
| Utils | helpers | Formatting and utility helpers |
| Text Art | text-art components | ASCII/text-art rendering helpers |
| Experimental | @hubspot/ui-extensions/experimental + escape hatches |
Prop labs, CRM hooks, Pages API probes, host-component render probes, and outbound hubspot.fetch tests |
Every demo exposes three actions in its header: View code (opens the component source on GitHub), Copy code (copies the example snippet), and — for playgrounds — a Customize drawer with live prop controls.
- A HubSpot account with developer/UI-Extensions access (a developer test account works well).
- Node.js 18+ and npm.
- The HubSpot CLI installed and authenticated:
npm install -g @hubspot/cli hs init # authenticate against your portal
git clone https://github.com/05bmckay/hs-uix-demos.git
cd hs-uix-demos
# install the page-bundle dependencies (hs-uix + ui-extensions)
npm install --prefix src/app/pages
# start local development against your authenticated portal
hs project devhs project dev builds the project, uploads it to your account, and opens a live-reloading session. To publish a build instead, run hs project upload.
Once installed, open the app in your HubSpot portal — its home page hosts the demo browser.
hs-uix-demos/
├── hsproject.json # HubSpot project config (name, srcDir, platformVersion)
└── src/
└── app/
├── app-hsmeta.json # App definition: auth, scopes, permitted URLs
└── pages/
├── home-hsmeta.json # Page entrypoint config (location: home)
├── Home.jsx # Page root → renders DemoBrowser
├── DemoBrowser.jsx # Tabbed gallery driven by a PACKAGES registry
├── playground.jsx # Shared header-slot context + useCustomizePanel drawer scaffold
├── data.jsx # Shared sample CRM-like data + formatters
├── *-demos.jsx # One file per package, each exporting an *_DEMOS array
├── text-art/ # Text Art demos, split per demo (index.jsx exports TEXT_ART_DEMOS)
└── package.json # Page bundle deps (hs-uix, @hubspot/ui-extensions)
Each *-demos.jsx file exports an array of demo objects consumed by DemoBrowser:
export const DATATABLE_DEMOS = [
{
id: "dt-playground",
name: "Interactive Table Playground",
description: "Storybook-style table preview with presets and live controls.",
package: "datatable",
Component: TablePlaygroundDemo,
githubUrl: "https://github.com/05bmckay/hs-uix/tree/main/packages/datatable",
sourceCode: `import { DataTable } from "hs-uix/datatable";`,
},
// ...
];DemoBrowser aggregates every package's array into ALL_DEMOS and renders one tab per entry in its PACKAGES registry (tab label + display order; the first demo in each package's array is its flagship hero). Tab content is structurally static for the lifetime of a Tabs mount: swapping the mounted demo in place inside the host Tabs component crashes the HubSpot renderer, so demo selection instead remounts the entire Tabs tree (a key on Tabs) with the selected demo rendered in its tab's panel — a fresh mount, which the host handles fine. Playground demos register their Customize drawer through useCustomizePanel in playground.jsx. The escape-hatch demos (escape-hatch-demos.jsx) have no tab of their own — they're merged into the Experimental tab's array.
App-level settings live in src/app/app-hsmeta.json:
- Distribution is
private. - Scopes requested:
oauth, plus read access to contacts, companies, and deals. - Permitted URLs (
permittedUrls) allow-list the outboundfetchandiframehosts used by the Experimental tab. If you add demos that call new external hosts, list them here or HubSpot will block the request.
The platform version is pinned in hsproject.json (platformVersion). hs-uix is pulled from npm at build time.
MIT © Carter McKay