npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bpinternal/integration-hub

v0.24.5

Published

Reusable Integration Hub for Botpress-style integration catalogs

Readme

@bpinternal/integration-hub

React UI components and optional Botpress runtime hooks for building integration hub experiences.

Two ways to use this package

UI only (@bpinternal/integration-hub)

You provide your own data and wire your own API calls. The package gives you layout components, cards, grids, filters, and setup wizard shells. Use this when you have a non-Botpress backend or want full control over data fetching.

Required peers: react, react-dom, framer-motion, @base-ui/react, clsx, tailwind-merge, lucide-react

UI + Botpress runtime (@bpinternal/integration-hub/runtime)

You pass a @botpress/client instance and the package handles fetching integrations, mapping data to UI types, and executing install/uninstall/enable/disable mutations. Use this when your app already talks to the Botpress API.

Additional peers: @botpress/client, @tanstack/react-query

@botpress/client and @tanstack/react-query are marked as optional peer dependencies. They are only needed when importing from ./runtime. UI-only consumers do not need to install them.


Quick start

Install

pnpm add @bpinternal/integration-hub react react-dom framer-motion @base-ui/react clsx tailwind-merge lucide-react

If using the Botpress runtime:

pnpm add @botpress/client @tanstack/react-query

@bpinternal/integration-hub/runtime includes a built-in configuration form renderer backed by @bpinternal/static-zui-form.

If your app does not already provide Radix-style design tokens, import the package fallback theme:

import '@bpinternal/integration-hub/theme.css'

Option A: Drop-in screen (fastest)

A single component that handles everything — fetching, mapping, catalog, detail views, mutations.

import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { BotpressIntegrationHub } from '@bpinternal/integration-hub/runtime'

const queryClient = new QueryClient()

export const IntegrationsPage = ({ client, workspaceId, botId }) => (
  <QueryClientProvider client={queryClient}>
    <BotpressIntegrationHub
      client={client}
      workspaceId={workspaceId}
      botId={botId}
      onInstall={(integration) => toast.success(`${integration.title} installed`)}
      onUninstall={(integration) => toast.success(`${integration.title} removed`)}
      onError={(error) => toast.error(error.message)}
    />
  </QueryClientProvider>
)

By default, BotpressIntegrationHub renders integration configuration fields using Static ZUI Form. Pass renderConfigurationForm only if you want to override this default behavior.

Option B: Hooks + UI components (more control)

Use the hooks for data/mutations but compose your own layout with the package's UI components.

import { useIntegrationHub, useIntegrationDetails } from '@bpinternal/integration-hub/runtime'
import {
  IntegrationHubBody,
  IntegrationGridCard,
  IntegrationInstalledList,
  IntegrationDetailsLayout,
  IntegrationCapabilitiesCards
} from '@bpinternal/integration-hub'

export const IntegrationsPage = ({ client, workspaceId, botId }) => {
  const [selectedId, setSelectedId] = useState(null)
  const [search, setSearch] = useState('')

  const hub = useIntegrationHub({ client, workspaceId, botId })
  const details = useIntegrationDetails({ client, workspaceId, botId, integrationId: selectedId })

  return (
    <IntegrationHubBody
      view="hub"
      searchInput={search}
      onSearchInputChange={setSearch}
      hubIntegrations={hub.integrations}
      sidebarIntegrations={hub.integrations}
      installedIntegrations={hub.installedIntegrations}
      isHubLoading={hub.isLoading}
      renderIntegrationCard={(integration) => (
        <IntegrationGridCard
          name={integration.title ?? integration.name}
          iconUrl={integration.iconUrl}
          installed={integration.installed}
          onClick={() => setSelectedId(integration.id)}
        />
      )}
      renderInstalledIntegrations={({ integrations, searchTerm, categoryOrder }) => (
        <IntegrationInstalledList
          integrations={integrations}
          searchTerm={searchTerm}
          categoryOrder={categoryOrder}
          onSelectIntegration={(i) => setSelectedId(i.id)}
        />
      )}
      renderIntegrationDetails={() => (
        <IntegrationDetailsLayout
          title={details.definition?.title ?? ''}
          iconUrl={details.definition?.iconUrl}
          onBack={() => setSelectedId(null)}
          onInstall={() => hub.install(selectedId)}
          isInstalled={Boolean(details.installedState)}
          isInstalling={hub.isInstalling}
        >
          <IntegrationCapabilitiesCards
            actionCards={details.capabilities.actionCards}
            eventCards={details.capabilities.eventCards}
          />
        </IntegrationDetailsLayout>
      )}
    />
  )
}

Option C: Mappers only (bring your own data layer)

Pure functions that convert @botpress/client types to the package's UI types. No React, no hooks. Use these if you already have your own React Query setup and just want to stop duplicating mapping logic.

import {
  toHubItem,
  toCapabilityCards,
  toConnectionMethods,
  getInstalledIntegrations,
  filterAndSortIntegrations
} from '@bpinternal/integration-hub/runtime'

// In your own React Query hook:
const integrations = await client.listPublicIntegrations({ version: 'latest' })
const bot = await client.getBot({ id: botId })

const installed = getInstalledIntegrations(bot.integrations)
const installedByName = new Map(installed.map((i) => [i.name, i]))

const hubItems = integrations.map((summary) => toHubItem({ summary, installedByName }))
const filtered = filterAndSortIntegrations({ integrations: hubItems, searchTerm: 'slack' })

Option D: UI only (no Botpress client)

Use the components with any data source. You handle all fetching and pass data as props.

import { IntegrationHubBody, IntegrationGridCard } from '@bpinternal/integration-hub'

const myIntegrations = [
  { id: '1', name: 'slack', title: 'Slack', description: 'Chat with Slack', iconUrl: '...' }
]

export const MyHub = () => (
  <IntegrationHubBody
    view="hub"
    searchInput={search}
    onSearchInputChange={setSearch}
    hubIntegrations={myIntegrations}
    sidebarIntegrations={myIntegrations}
    installedIntegrations={[]}
    renderIntegrationCard={(integration) => (
      <IntegrationGridCard name={integration.title} iconUrl={integration.iconUrl} />
    )}
    renderInstalledIntegrations={() => <div>No installed integrations</div>}
    renderIntegrationDetails={(id) => <div>Details for {id}</div>}
  />
)

API reference

useIntegrationHub(options)

Hook that fetches the integration catalog and bot state, and provides mutation functions.

Options:

| Param | Type | Description | |---|---|---| | client | Client | A configured @botpress/client instance | | workspaceId | string | Workspace ID | | botId | string | Bot ID | | categoryResolver? | (integration) => string \| null | Override default category mapping | | enabled? | boolean | Disable fetching (default true) |

Returns:

| Field | Type | Description | |---|---|---| | integrations | BotpressIntegrationHubItem[] | All public + private integrations with installed/enabled/hasUpdate computed | | installedIntegrations | BotpressIntegrationHubItem[] | Only integrations installed on the bot | | isLoading | boolean | Initial load in progress | | error | Error \| null | Fetch error | | install(id) | (id: string) => Promise<void> | Install integration on the bot | | uninstall(id) | (id: string) => Promise<void> | Remove integration from the bot | | enable(id) | (id: string) => Promise<void> | Enable installed integration | | disable(id) | (id: string) => Promise<void> | Disable installed integration | | upgrade(id) | (id: string) => Promise<void> | Upgrade integration to latest version | | refresh() | () => void | Re-fetch all data | | isInstalling | boolean | Install mutation in progress | | isUninstalling | boolean | Uninstall mutation in progress | | isEnabling | boolean | Enable mutation in progress | | isDisabling | boolean | Disable mutation in progress | | isUpgrading | boolean | Upgrade mutation in progress |

useIntegrationDetails(options)

Hook that fetches a single integration's full definition, bot-installed state, and capabilities.

Options:

| Param | Type | Description | |---|---|---| | client | Client | A configured @botpress/client instance | | workspaceId | string | Workspace ID | | botId | string | Bot ID | | integrationId | string \| null | Integration to load (null = disabled) | | enabled? | boolean | Disable fetching (default true) |

Returns:

| Field | Type | Description | |---|---|---| | definition | Integration \| null | Full integration definition (actions, events, channels, config schema) | | installedState | BotpressInstalledIntegration \| null | Bot's installed state (enabled, config, webhookUrl) or null if not installed | | capabilities.actionCards | IntegrationCapabilityCardItem[] | Action cards ready for IntegrationCapabilitiesCards | | capabilities.eventCards | IntegrationCapabilityCardItem[] | Event cards | | capabilities.channelCards | IntegrationCapabilityCardItem[] | Channel cards | | connectionMethods | IntegrationConnectionMethodOption[] | Available connection methods (OAuth, named configs, sandbox) | | selectedConnectionMethod | string \| null | Currently active connection method | | isLoading | boolean | Fetch in progress | | error | Error \| null | Fetch error | | saveConfig(config) | (config: Record<string, unknown>) => Promise<void> | Save integration configuration | | toggleEnabled() | () => Promise<void> | Toggle enabled/disabled state | | setConnectionMethod(method) | (method: string \| null) => Promise<void> | Switch connection method | | refresh() | () => void | Re-fetch details | | isSavingConfig | boolean | Config save in progress | | isTogglingEnabled | boolean | Toggle mutation in progress | | isSettingConnectionMethod | boolean | Connection method mutation in progress |

BotpressIntegrationHub (composed screen)

Drop-in component that wires useIntegrationHub + useIntegrationDetails + all UI components. Manages view/search/selection/tab state internally.

Required props: client, workspaceId, botId

Optional overrides:

| Prop | Type | Description | |---|---|---| | initialView | 'hub' \| 'installed' | Starting view (default 'hub') | | categories | IntegrationHubCategory[] | Override default categories | | bannerSlides | IntegrationHubBannerSlide[] | Override promotional banner content | | bannerIconUrl | string | Banner image URL | | filterMenuLabels | Partial<IntegrationHubFilterMenuLabels> | Override filter menu text | | categoryResolver | (integration) => string \| null | Custom category mapping | | disableInstallation | boolean | Hide install buttons | | hasWriteAccess | boolean | Disable mutation buttons when false | | noResultsDocumentationLink | { href, label? } | Link shown in empty search results | | oauthTemplateData | Record<string, unknown> | Extra payload merged into client.runVrl when generating OAuth authorization URLs. If env is omitted, the runtime infers production for *.cloud API URLs and preview otherwise. |

Render slots (for things the package cannot own):

| Slot | Props you receive | Purpose | |---|---|---| | renderConfigurationForm | { definition, installedState, selectedConnectionMethod, onSave, isSaving } | Override the default Static ZUI configuration form renderer. | | renderConnectionsContent | { definition, installedState, connectionMethods, selectedConnectionMethod, onConnectionMethodChange } | Override the default runtime connection UI (OAuth/manual flows). | | renderDetailPanel | { definition, selectedCard, onClose } | Render the side panel when a capability card is selected (e.g. schema viewer). |

Callbacks:

| Callback | Type | When it fires | |---|---|---| | onInstall | (integration) => void | After successful install | | onUninstall | (integration) => void | After successful uninstall | | onEnable | (integration) => void | After successful enable | | onDisable | (integration) => void | After successful disable | | onUpgrade | (integration) => void | After successful upgrade | | onError | (error: Error) => void | On any mutation error |

Mapper functions

Pure functions (no React) exported from @bpinternal/integration-hub/runtime:

| Function | Description | |---|---| | toHubItem({ summary, installedByName, categoryResolver? }) | Convert a @botpress/client integration summary + bot state into an IntegrationHubItem | | toInstalledItem({ installedIntegration, summary?, categoryResolver? }) | Convert a bot's installed integration entry into an IntegrationHubItem | | toCapabilityCards(definition) | Extract actions, events, and channels from a full integration definition into card arrays | | toConnectionMethods(definition) | Extract available connection methods (OAuth, named configs, sandbox) from a definition | | getInstalledIntegrations(botIntegrations) | Convert the raw bot.integrations map into a typed array | | findInstalledIntegration({ installedIntegrations, integrationId, integrationName? }) | Find a specific installed integration by ID or name | | filterAndSortIntegrations({ integrations, searchTerm, visibility?, verification?, sortMethod? }) | Filter and sort hub items by search, visibility, verification status, and sort method | | resolveCategory(integration, categoryResolver?) | Resolve an integration's category with optional custom resolver | | isOfficialWorkspace(handle) | Check if a workspace handle is the official Botpress workspace | | dedupeById(items) | Remove duplicate items by ID |


What this package does NOT include

These are the consumer's responsibility:

| Thing | Why | What to do | |---|---|---| | Auth / session | Every app handles auth differently | Create your own @botpress/client instance with your token and pass it to hooks | | Custom OAuth UX | The package includes a default OAuth flow, but app-specific redirects and UX can vary | Use oauthTemplateData or override with renderConnectionsContent / IntegrationOAuthButton | | Custom config form behavior | The package ships a Static ZUI default renderer, but apps may need custom controls/validation/UX | Provide renderConfigurationForm only when overriding the default | | Routing / URL sync | Too framework-specific (React Router, TanStack Router, Next.js) | Manage selectedIntegrationId and view state yourself and map to your router | | Toast notifications | Every app has its own toast system | Use onInstall, onUninstall, onError callbacks to trigger your toasts | | Analytics / tracking | App-specific telemetry | Use mutation callbacks (onInstall, onUninstall, onEnable, onDisable, onUpgrade) and your own UI-level tracking hooks | | Feature flags | App-specific gating | Pass disableInstallation or hasWriteAccess booleans |


Customizing defaults

The package ships with generic defaults. Override them for your product:

  • bannerSlides — promotional banner content (pass your own slides or an empty array to hide)
  • categories — integration categories with icons (defaults to 13 common categories)
  • filterMenuLabels — deep-merge with defaults to change labels like { verification: { official: 'Made by Botpress' } }
  • noResultsDocumentationLink — link shown when search has no results
  • formatGithubLabel on IntegrationSetupMetadataBar — custom GitHub URL display formatting
  • categoryResolver — function to override how integrations are assigned to categories

UI components

All exported from @bpinternal/integration-hub:

| Component | Purpose | |---|---| | IntegrationHubBody | Full hub page: category sidebar, search, banner carousel, responsive card grid, installed list, detail pane routing | | IntegrationHubFilterMenu | Dropdown filter menu (visibility, verification, sort) with active filter pills | | IntegrationGridCard | Integration card with icon, name, description, author, badges | | IntegrationInstalledList | Sorted/filtered list of installed integrations with enabled/disabled status | | IntegrationDetailsLayout | Detail page shell: back button, header, Install/Uninstall/Enable/Disable/Upgrade buttons, side panel slot | | IntegrationSetupWizardLayout | Setup wizard: "setup mode" numbered steps and "configured mode" tabs | | IntegrationSetupMetadataBar | Version, GitHub link, About button row | | IntegrationConnectionMethodSelector | Dropdown to pick connection method (OAuth, named config, sandbox) | | IntegrationWebhookUrlField | Read-only webhook URL with copy button and tooltip | | IntegrationConfigurationSection | Configuration wrapper with title, webhook field, blocked state, and children slot | | IntegrationCapabilitiesCards | Action/event/channel capability card grid with selection state |


Styling requirements

1. Import package CSS

The package ships precompiled component styles in theme.css. Import it once in your app entrypoint:

import '@bpinternal/integration-hub/theme.css'

You do not need to add @bpinternal/integration-hub to Tailwind content globs for runtime styles. Tailwind scanning of package source is only useful when developing against unpublished local source.

2. CSS custom properties

Components use Radix-style color tokens. Either use @radix-ui/themes or define these variables:

:root {
  --gray-1: #ffffff;
  --gray-2: #f8f9fa;
  --gray-3: #f1f3f5;
  --gray-5: #e1e5ea;
  --gray-6: #d0d7de;
  --gray-7: #bfc8d3;
  --gray-8: #9aa4b2;
  --gray-9: #7a8594;
  --gray-10: #6b7280;
  --gray-11: #4b5563;
  --gray-12: #111827;

  --gray-a2: rgba(17, 24, 39, 0.04);
  --gray-a3: rgba(17, 24, 39, 0.07);
  --gray-a4: rgba(17, 24, 39, 0.1);
  --gray-a5: rgba(17, 24, 39, 0.14);
  --gray-a6: rgba(17, 24, 39, 0.2);
  --gray-a8: rgba(17, 24, 39, 0.4);

  --blue-a2: rgba(37, 99, 235, 0.12);
  --blue-8: #3b82f6;
  --blue-11: #1d4ed8;

  --grass-3: #ecfdf3;
  --grass-5: #a7f3d0;
  --grass-10: #16a34a;

  --accent-9: #0ea5e9;
  --accent-11: #0284c7;
}

3. Container queries

IntegrationHubBody uses native CSS container queries for responsive grids. If you rebuild package styles from source in a Tailwind v3 app, add:

import containerQueries from '@tailwindcss/container-queries'
export default { plugins: [containerQueries] }

Build

pnpm --filter @bpinternal/integration-hub build
pnpm --filter @bpinternal/integration-hub type-check

Publish

pnpm --filter @bpinternal/integration-hub exec npm version patch
pnpm --filter @bpinternal/integration-hub exec npm publish --access public

prepack runs clean && build automatically before publish.

Note: Depending on options passed to the botpress client (x-multiple-integrations = true), the integrations from Botpress Cloud are keyed by their ALIAS, not their ID. For edits that intend to use the integration ID, make sure to explicitly use the .id parameter.