@centaurisystems/launchpad-sdk
v1.1.2
Published
Launchpad component SDK: registerComponent, usePortalContext, and the locked component build toolchain. SDK v1 targets React 19.
Maintainers
Readme
@centaurisystems/launchpad-sdk
Build React components for Launchpad developer portals: register them, read
portal data, and build them with the locked toolchain. Publish with one
command via @centaurisystems/cli —
versioned per environment, rolled back in seconds, disabled with one click.
Install
npm install --save-dev @centaurisystems/launchpad-sdk @centaurisystems/cliUse
import { registerComponent, usePortalContext } from "@centaurisystems/launchpad-sdk";
function PricingCalculator() {
const { environment, theme } = usePortalContext();
return <div style={{ color: theme.colors.primary }}>Running in {environment}</div>;
}
registerComponent({
name: "pricing-calculator",
displayName: "Pricing Calculator",
component: PricingCalculator,
props: { title: { type: "string", label: "Title" } }
});Local dev/testing: wrap with MockPortalProvider. At portal runtime this
module is replaced by the portal's live implementation.
The manifest the platform stores comes from the "launchpad" field of your
package.json (copied to dist/launchpad.json by centauri components build).
Your code is never executed server-side.
Theming: follow the portal's light/dark toggle
usePortalContext().theme is live. On portals with light/dark support:
theme.colors.background_color/text_color/surface_color/border_colorare mode-resolved: complete CSS color strings (e.g.hsl(211 100% 59%)) whose values flip — re-rendering your component — when the visitor toggles dark mode. Style your surfaces with these (backgrounds, text, cards, borders) so your component follows the toggle automatically. Any of the four may be absent (older portals, or a portal that can't resolve them), so always keep your own fallback.theme.mode("light" | "dark", optional) is for discrete switches the resolved colors can't express — picking a logo asset or a chart palette. Treatundefinedas light.- Brand
*_colorkeys (primary_color,secondary_color,key_color, …) are raw brand tokens and do not flip with the mode — use them for accents, never for surfaces.
function StatusCard() {
const { theme } = usePortalContext();
const { colors, mode } = theme;
const style: React.CSSProperties = {
// Mode-resolved keys with graceful fallbacks — flips live with the toggle.
background: colors.surface_color ?? "#f8fafc",
color: colors.text_color ?? "#0f172a",
border: `1px solid ${colors.border_color ?? "#e2e8f0"}`,
// Brand accent: intentionally NOT mode-resolved.
borderTopColor: colors.primary_color ?? "#6366f1"
};
return (
<div style={style}>
<img src={mode === "dark" ? logoDark : logoLight} alt="" />
</div>
);
}Site placement
By default a component is a block: something a portal admin drags onto a
page in the page builder and configures per placement. Set "placement":
"site" in the manifest instead to have the portal mount your component once
on every portal page, rather than as a page-builder block. Omit the field
(or set it to "block") for the default page-block behavior — the build
never injects a default into dist/launchpad.json when it's absent from
your source.
{
"launchpad": {
"name": "pageview-tracker",
"displayName": "Pageview Tracker",
"placement": "site",
"description": "Site-wide, invisible pageview/click tracking."
}
}Site-placement components:
- are excluded from the page builder's block palette — there's no page slot to drag them onto
- are typically headless: render
nullalways, since nothing anchors visible output for a component with no page slot - must clean up every listener, timer, and patch (e.g. a monkeypatched
history.pushState) on unmount. The portal can mount and unmount a site component repeatedly — navigation-rule changes, a kill switch — so nothing it sets up may outlive one mount/unmount cycle, and it must never touch state after unmounting.
See examples/pageview-tracker for a full reference implementation (SPA
navigation tracking via patched history methods, sampling, sendBeacon
delivery).
Slot placement: replace the portal's built-in chrome
Set "placement" to "slot:header", "slot:footer", or "slot:menu" to
have the portal render your component instead of its built-in chrome
for that region. footer is the bottom chrome; header is the entire top
chrome bar; menu is the primary navigation region inside the built-in
header bar (a header replacement subsumes the menu — enable one or the
other). Slot regions are a closed set defined by the portal layout:
any other slot:* value is rejected by the build and by the platform at
publish time.
{
"launchpad": {
"name": "custom-footer",
"displayName": "Custom Footer",
"placement": "slot:footer",
"description": "Replaces the portal's built-in footer."
}
}What "replace" means for your component:
- Render a complete region, not a fragment. The built-in chrome for that region is gone while your component is enabled and in scope — everything the region should show (navigation, copyright, links) is yours to render.
- The portal fails safe, but don't rely on it. If your bundle fails to load, fails its integrity check, or your component throws (repeatedly — a circuit breaker stops re-mount attempts for the session), the portal falls back to its own built-in chrome for that region, and admin routes always keep built-in chrome. That fallback is a recovery mechanism, not a feature: a flickering half-replaced region is still a broken portal.
- Keep it accessible. You are now the portal's chrome. Use the real
landmarks (
<footer role="contentinfo">,<header role="banner">, a<nav>with anaria-labelfor menus), real<a>elements for links (never click-handler divs), and visible focus states — keyboard and screen-reader users navigate portals by these landmarks. - Same settings surface as site components. Portal admins configure
your manifest-declared props plus path (
include/excludeprefixes) and audience (all/authenticated/anonymous) rules per environment; out of scope, the built-in chrome renders. Among several enabled components targeting the same slot, only one renders per page (admin-controlled ordering). - Same portal context.
usePortalContext()works exactly as for blocks — including the livetheme.modeand the mode-resolvedbackground_color/text_color/surface_color/border_colorkeys (see "Theming" above), so your chrome follows the visitor's light/dark toggle. - Like site components, slot components are excluded from the page builder's block palette, and clean unmount rules apply: remove every listener and timer in effect cleanup.
See examples/custom-footer for a full reference implementation (complete
accessible footer, defensive link-list parsing, live theme mapping).
Versioning and deprecation policy
The SDK major version pins to a React major version. SDK v1 → React 19
(peer dependency react@^19).
When Launchpad portals upgrade to a new React major, a new SDK major is released, and the previous SDK major remains supported in portals for a minimum of 6 months from the portal upgrade announcement. Rebuild and republish your components against the new SDK major within that window.
Node API
buildComponent(opts)from@centaurisystems/launchpad-sdk/build— the locked esbuild config used bycentauri components build.scaffoldComponent(opts)from@centaurisystems/launchpad-sdk/scaffold— thecreate-launchpad-componenttemplate used bycentauri components init.
