Skip to content
GitHubXDiscord

Triggery

Write business logic, not boilerplate.

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.

src/triggers/message.trigger.ts
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.

src/Chat.tsx
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

For SolidJS use @triggery/solid, for Vue 3 use @triggery/vue — the API is identical.

Read the Getting Started guide See it in a real recipe Open in StackBlitz Open in CodeSandbox

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

TriggeryRedux SagaRTK listenerMiddlewareXStateRxJS
Hook-first APIyesnopartialyesno
Per-scenario fileyesnopartialyesno
Cross-frameworkyesRedux-onlyRedux-onlyyesyes
Inline-generic schemayesnonoyesn/a
Built-in DevToolsyesexternalRTK DevToolsyesexternal
Bundle (core, gzip)< 6kB~17kBbundled 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