Framework-agnostic core
Zero runtime dependencies. Use @triggery/core directly from Node, plain JavaScript, or any framework. Bindings are thin lifecycle layers — about 50 lines each.
One file describes one scenario. Events go in, conditions gate the world, actions perform side effects. The same hook-shaped API works in React, Solid and Vue.
import { createTrigger } from '@triggery/core';
type Settings = { sound: boolean; notifications: boolean };
type Payload = { author: string; text: string; channelId: string };
export const messageTrigger = createTrigger<{
events: { 'new-message': Payload };
conditions: { settings: Settings; activeChannelId: string | null };
actions: { showToast: { title: string; body: string }; playSound: 'beep' };
}>({
id: 'message-received',
required: ['settings'],
handler({ event, conditions, actions, check }) {
if (conditions.activeChannelId === event.payload.channelId) return;
if (!check.is('settings', s => s.notifications)) return;
actions.debounce(800).playSound?.('beep');
actions.showToast?.({ title: event.payload.author, body: event.payload.text });
},
});Components stay focused on their own port. None of them reach into another’s state.
import { useEvent, useCondition, useAction } from '@triggery/react';
import { messageTrigger } from './triggers/message.trigger';
// Producer: a chat input fires the event.
const fireNewMessage = useEvent(messageTrigger, 'new-message');
// Provider: a settings panel exposes a snapshot via a getter.
useCondition(messageTrigger, 'settings', () => settings, [settings]);
// Reactor: a toast component subscribes to the action.
useAction(messageTrigger, 'showToast', payload => toast.success(payload.title, { description: payload.body }));No props drilled, no useEffect chains, no shared context bus. Edit the trigger file and the whole reaction changes — every component stays the same.
Framework-agnostic core
Zero runtime dependencies. Use @triggery/core directly from Node, plain JavaScript, or any framework. Bindings are thin lifecycle layers — about 50 lines each.
React · Solid · Vue
The same hook API in every binding. Switch your stack without rewriting the orchestration layer.
Type-safe by design
One inline-generic schema, three typed surfaces — events, conditions, actions. No as casts, no .d.ts files, no codegen.
Test without React
Trigger handlers are nearly pure functions of a snapshot. Mock conditions, mock actions, fire events. RTL is optional.
DevTools built-in
An inspector ring buffer ships in every runtime. Bridge to Redux DevTools, drop in <InspectorView>, or replay sessions.
ESLint + codemods
8 lint rules catch the common mistakes. Codemods migrate from useEffect, RTK listenerMiddleware, and (1.0) Redux Saga.
pnpm add @triggery/core @triggery/react npm install @triggery/core @triggery/react yarn add @triggery/core @triggery/react bun add @triggery/core @triggery/react For SolidJS use @triggery/solid, for Vue 3 use @triggery/vue — the API is identical.
Triggery is the only orchestration library where the same scenario file makes sense to a junior engineer on Monday and a senior engineer reviewing it on Friday.
— early adopter, 2026
| Triggery | Redux Saga | RTK listenerMiddleware | XState | RxJS | |
|---|---|---|---|---|---|
| Hook-first API | yes | no | partial | yes | no |
| Per-scenario file | yes | no | partial | yes | no |
| Cross-framework | yes | Redux-only | Redux-only | yes | yes |
| Inline-generic schema | yes | no | no | yes | n/a |
| Built-in DevTools | yes | external | RTK DevTools | yes | external |
| Bundle (core, gzip) | < 6kB | ~17kB | bundled w/ RTK | ~16kB | ~46kB |
For longer-form comparisons see Migration → Comparison.
Triggery is at the 0.9.x release-candidate window — feature freeze, last round of community testing before 1.0. The runtime has been used in production for orchestration since early 2026 and the test coverage targets are enforced in CI (lines ≥ 95%, branches ≥ 85%). See the public roadmap for what’s next.
Sponsor the project