@triggery/react
React bindings for Triggery. Same hook shape as @triggery/solid and @triggery/vue. Zero runtime dependencies — a thin lifecycle layer over @triggery/core.
Install
Section titled “Install”pnpm add @triggery/core @triggery/react npm install @triggery/core @triggery/react yarn add @triggery/core @triggery/react bun add @triggery/core @triggery/react Peer deps: react >= 18.0.0, react-dom >= 18.0.0.
What’s inside
Section titled “What’s inside”| Export | Purpose |
|---|---|
useEvent | Typed event emitter, stable identity. |
useCondition | Register a pull-only condition getter. |
useAction | Register an action handler. |
useInlineTrigger | One-off triggers inside a component. |
useInspect / useInspectHistory | Snapshot of the latest run / ring-buffer history. |
createNamedHooks | Derive useNewMessageEvent, useUserCondition, … from a trigger. |
<TriggerRuntimeProvider> | Inject a custom runtime into a subtree. |
<TriggerScope> | Scope condition/action registrations to a feature subtree. |
Quick start
Section titled “Quick start”Three small files. The trigger reads like a spec, components only know about their own port.
1. Define the trigger
Section titled “1. Define the trigger”src/triggers/message.trigger.ts
import { createTrigger } from '@triggery/core';
type Settings = { sound: boolean; notifications: boolean };
export const messageTrigger = createTrigger<{
events: { 'new-message': { text: string; author: string } };
conditions: { settings: Settings };
actions: { showToast: { title: string; body: string } };
}>({
id: 'message-received',
events: ['new-message'],
required: ['settings'],
handler({ event, conditions, actions }) {
if (!conditions.settings) return;
if (!conditions.settings.notifications) return;
actions.showToast?.({
title: event.payload.author,
body: event.payload.text,
});
},
});2. Wrap the tree
Section titled “2. Wrap the tree”src/main.tsx
import { createRuntime } from '@triggery/core';
import { TriggerRuntimeProvider } from '@triggery/react';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { App } from './App';
const runtime = createRuntime();
createRoot(document.getElementById('root')!).render(
<StrictMode>
<TriggerRuntimeProvider runtime={runtime}>
<App />
</TriggerRuntimeProvider>
</StrictMode>,
);3. Wire components
Section titled “3. Wire components”src/App.tsx
import { useAction, useCondition, useEvent } from '@triggery/react';
import { useState } from 'react';
import { messageTrigger } from './triggers/message.trigger';
function SettingsProvider() {
const [settings] = useState({ sound: true, notifications: true });
useCondition(messageTrigger, 'settings', () => settings, [settings]);
return null;
}
function Chat() {
const fire = useEvent(messageTrigger, 'new-message');
return (
<button type="button" onClick={() => fire({ text: 'hi', author: 'Alice' })}>
send
</button>
);
}
function Toast() {
useAction(messageTrigger, 'showToast', ({ title, body }) => console.log(`[${title}] ${body}`));
return null;
}
export function App() {
return (
<>
<SettingsProvider />
<Chat />
<Toast />
</>
);
}Click the button — Toast logs. Toggle settings.notifications to false — silent.
Recipes
Section titled “Recipes” Counter The hello-world recipe.
Notification pipeline Toast + sound + badge in three components.
Debounced search take-latest + AbortSignal for autocomplete.
WebSocket sync Real-time updates without component coupling.
SSR / RSC
Section titled “SSR / RSC”The hooks return no-ops on the server. The actual subscription happens in useEffect, which means there’s no hydration mismatch. See the SSR guide and React Server Components guide.
Related packages
Section titled “Related packages” @triggery/core The runtime this binding wraps.
@triggery/solid Identical hook shape for SolidJS.
@triggery/vue Identical hook shape for Vue 3.
@triggery/zustand Adapter: read a Zustand store from a condition.
@triggery/redux Adapter: read a Redux store from a condition.
@triggery/testing Test the trigger without rendering React.