React bindings for the Pandino framework. Start the runtime, install bundles, and consume services from React components using a small set of hooks and helpers.
┌──────────────────────────┐
│ @pandino/pandino │
│ (service registry, │
│ bundles, SCR) │
└────────────┬─────────────┘
│ starts & wraps
▼
┌──────────────────────────┐
│ @pandino/react-hooks │
│ <PandinoProvider> │
│ useService / useBundle │
│ useServiceTracker ... │
└────────────┬─────────────┘
│ consumed by
▼
Your React tree
This package wraps the Pandino framework with a React context and exposes idiomatic hooks for service discovery, dynamic registration, and bundle introspection. It is the recommended integration layer for React applications built on Pandino.
npm install @pandino/pandino @pandino/react-hooks reflect-metadataPeer dependencies: react and react-dom (v19+), @pandino/pandino.
Import reflect-metadata once at the entry point of your application:
import 'reflect-metadata';// main.tsx
import 'reflect-metadata';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { PandinoProvider } from '@pandino/react-hooks';
import App from './App';
createRoot(document.getElementById('root')!).render(
<StrictMode>
<PandinoProvider
bundles={[
import('./bundles/greeting-bundle'),
// ...more bundle modules
]}
>
<App />
</PandinoProvider>
</StrictMode>,
);The provider bootstraps the framework, installs every bundle module passed via bundles, and makes the BundleContext available through React context. Each entry is a dynamic import() returning a Pandino BundleModule — typically produced by @pandino/rollup-bundle-plugin.
// App.tsx
import { useService } from '@pandino/react-hooks';
interface GreetingService {
sayHello(name: string): string;
}
export default function App() {
const { service, loading, error } = useService<GreetingService>('GreetingService');
if (loading) return <p>Loading...</p>;
if (error) return <p>Error: {error.message}</p>;
if (!service) return <p>GreetingService unavailable</p>;
return <h1>{service.sayHello('React')}</h1>;
}Resolves a single service by interface name (and optional LDAP filter). Releases the service reference automatically when the component unmounts or the reference changes.
const { service, loading, error } = useService<PaymentService>(
'PaymentService',
'(region=EU)',
);Returns { service, loading, error }.
Tracks all services that match an interface (and optional filter). Re-renders whenever services appear, change, or disappear.
const { services, loading, error } = useServiceTracker<PluginService>('PluginService');
return (
<ul>
{services.map((plugin, i) => <li key={i}>{plugin.name}</li>)}
</ul>
);Registers a service for the lifetime of the component. Useful when a React component itself wants to contribute a service to the registry.
const { isRegistered, updateProperties } = useRegisterService('AnalyticsSink', {
track: (event) => console.log(event),
}, { priority: 10 });The service is unregistered automatically on unmount.
Looks up a bundle by numeric id or symbolic name and exposes its lifecycle state.
const { bundle, loading, error } = useBundle('com.example.database');
if (bundle) console.log(bundle.getState());Low-level escape hatches that return the raw BundleContext or the full context value ({ framework, bundleContext, isInitialized, error }). Use them when you need APIs that don't have a dedicated hook.
const context = useBundleContext();
const refs = context?.getServiceReferences('EventHandler', '(event.topics=user/*)');Root provider. Accepts:
| Prop | Type | Purpose |
|---|---|---|
bundles |
Array<Promise<BundleModule>> |
Bundle modules to install and start |
bootstrapConfig |
BootstrapConfig (optional) |
Forwarded to the Pandino OSGiBootstrap |
children |
ReactNode |
Your application |
Renders a bundle's id, symbolic name, version, and state. Pass a children render prop to customise the output; otherwise a default table is rendered.
<BundleInfo bundleIdOrName="com.example.database" />Render-prop wrapper around useService. Handy when you prefer composition over hooks.
<ServiceConsumer<GreetingService> serviceClass="GreetingService">
{({ service, loading }) =>
loading ? <Spinner /> : <h1>{service?.sayHello('world')}</h1>
}
</ServiceConsumer>Resolves a service that is itself a React component (or React element) and renders it with the remaining props. Useful for plugin-style UIs where the rendered component is provided by a bundle.
<ComponentProxy serviceClass="DashboardWidget" filter="(slot=primary)" user={user} />function PremiumFeature({ children }: { children: React.ReactNode }) {
const { service: auth } = useService<AuthService>('AuthService');
const { service: flags } = useService<FeatureFlags>('FeatureFlags');
if (!auth || !flags) return <p>Loading...</p>;
if (!auth.isAuthenticated()) return <p>Please sign in.</p>;
if (!flags.isEnabled('premium')) return <p>Feature unavailable.</p>;
return <>{children}</>;
}function Toolbar() {
const { services: actions } = useServiceTracker<ToolbarAction>('ToolbarAction');
return (
<nav>
{actions.map((a) => (
<button key={a.id} onClick={a.invoke}>{a.label}</button>
))}
</nav>
);
}const { service: logger } = useService<LogService>('LogService', '(log.target=console)');@pandino/pandino— Core runtime. Every type the hooks return (BundleContext,ServiceReference,Bundle, ...) comes from there.@pandino/decorators— Declare the services you consume here.@pandino/rollup-bundle-plugin— Package your bundles so they can be passed to<PandinoProvider bundles={...} />.
Eclipse Public License - v 2.0