@kb-labs/studio-plugin-tools
v2.89.0
Published
Build tooling for plugin Studio pages — Rspack config for Module Federation remotes
Downloads
22,726
Readme
@kb-labs/studio-plugin-tools
Build tooling for Studio plugin pages (Module Federation remotes).
Uses Rspack with @module-federation/enhanced for native MF support.
Usage
// rspack.studio.config.mjs
import { createStudioRemoteConfig } from '@kb-labs/studio-plugin-tools';
export default await createStudioRemoteConfig({
name: 'myPlugin',
exposes: {
'./MyPage': './src/studio/pages/MyPage.tsx',
},
});Plugin page conventions
export default function MyPage() { ... }— default export only, no named export of the same component- Import all UI from
@kb-labs/sdk/studio— never from@kb-labs/studio-ui-kitdirectly - No top-level side effects in the page module
Available hooks
All hooks are available via import { ... } from '@kb-labs/sdk/studio':
| Hook | Purpose |
|------|---------|
| useData<T>(endpoint, opts?) | REST GET with caching and polling |
| useMutateData<I,O>(endpoint, method?) | REST POST/PUT/PATCH/DELETE mutations |
| useSSE<T>(endpoint, opts?) | Server-sent events with reconnect |
| useInfiniteData<T>(endpoint, opts) | Cursor/offset pagination |
| useWebSocket<S,R>(url, opts?) | Bidirectional WebSocket |
| useEventBus() | Cross-plugin pub/sub communication |
| usePermissions() | Permission checks |
| useNavigation() | Programmatic routing |
| useNotification() | Toast/alert notifications |
| useTheme() | Design tokens and theme mode |
| usePage() | Page context (pageId, pluginId, permissions) |
Troubleshooting
Objects are not valid as a React child / mountIndeterminateComponent
Symptom: Plugin page crashes immediately on first load with one of:
Objects are not valid as a React child (found: object with keys {$$typeof, type, key, ref, props})TypeError: Cannot read properties of null (reading 'useRef')- Error points to
mountIndeterminateComponentin the host'smain.js
These errors all mean one thing: React version mismatch between the plugin bundle and the Studio host.
Root cause — React 19 bundled into the plugin:
pnpm resolves "react": ">=18.0.0" (from peer deps) to the latest available React, which may be React 19. Module Federation requires all remotes to share the same React version as the host (which uses React 18). When React 19 code ends up in the plugin bundle, Symbol.for("react.transitional.element") is used instead of Symbol.for("react.element"), and elements from one React can't be reconciled by the other.
createStudioRemoteConfig catches this at build time:
Since v0.1.x, createStudioRemoteConfig validates the resolved React version before building. If React 19 is detected, the build fails immediately with a clear error:
❌ Studio plugin build failed: React 19.x.x detected.
Studio host runs React 18. When a plugin bundles React 19, elements use
Symbol.for('react.transitional.element') which React 18 cannot reconcile,
causing "Objects are not valid as a React child" crash at runtime.
Fix: pin React 18 in your plugin's devDependencies: ...Fix: Pin React 18 in devDependencies of the plugin's CLI package:
// packages/my-plugin-cli/package.json
{
"devDependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1"
}
}Then reinstall and rebuild the widget:
pnpm --filter @kb-labs/my-plugin-cli install
pnpm --filter @kb-labs/my-plugin-cli run build:studioReference plugins: @kb-labs/commit-cli, @kb-labs/agent-cli — both pin react@^18.3.1 in devDependencies.
Objects are not valid as a React child — stale ui-kit dist
Symptom: Plugin crashes after ui-kit source was changed, but studio-ui-kit dist was not rebuilt.
The Studio dev server serves @kb-labs/studio-ui-kit from its compiled dist (dist/index.js), not from TypeScript source. Plugins also bundle a fallback copy of ui-kit from the same dist. If the source is fixed but the dist is stale, both the host's shared scope and the plugin's fallback bundle serve the old broken code.
A SubComponent alias like export const UIDescriptionsItem = AntDescriptions.Item compiles to an antd internal object — not a React function component. When React tries to render <UIDescriptionsItem />, it receives this object as the component type and throws.
Fix: Rebuild studio-ui-kit dist, then restart Studio:
pnpm --filter @kb-labs/studio-ui-kit build
kb-dev restart studioRule: Any time you change studio-ui-kit source, you must rebuild its dist before changes are visible.
Plugin navigation — error from previous plugin "leaks" to next
Symptom: Navigating from a broken plugin page to a working one — the working page shows the same crash error. Clicking Retry fixes it.
Root cause: PageErrorBoundary is a class component that holds error state. Without a key prop on PageContainer, React reuses the same boundary instance between routes and the error state persists.
Fix: Already applied in plugin-page-v2.tsx — PageContainer renders with key={${plugin.remoteName}::${page.entry}} so React fully unmounts/remounts on navigation.
Double export causes MF bundling issues
Symptom: Plugin page crashes with component type errors.
Wrong:
export function MyPage() { ... } // named export
export default MyPage; // + default export of same componentRight:
function MyPage() { ... } // no named export
export default MyPage;MF expose bundles the module graph starting from the exposed entry. A named export of the same component can confuse the bundler into including two instances of the component in different chunks.
