@triggery/solid
SolidJS bindings for Triggery. Same hook shape as @triggery/react and @triggery/vue, native to Solid’s signals and onCleanup lifecycle. Zero runtime dependencies — a thin lifecycle layer over @triggery/core.
Install
Section titled “Install”pnpm add @triggery/core @triggery/solid solid-js npm install @triggery/core @triggery/solid solid-js yarn add @triggery/core @triggery/solid solid-js bun add @triggery/core @triggery/solid solid-js Peer deps: solid-js >= 1.8.0.
What’s inside
Section titled “What’s inside”| Export | Purpose |
|---|---|
useEvent | Typed event emitter, stable identity across the component lifetime. |
useCondition | Register a pull-only condition getter. Reads signals naturally. |
useAction | Register an action handler. Auto-cleans on onCleanup. |
useInlineTrigger | One-off triggers declared inside a component. |
<TriggerRuntimeProvider> | Inject a custom runtime via Solid’s context API. |
<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”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.notifications) return;
actions.showToast?.({
title: event.payload.author,
body: event.payload.text,
});
},
});2. Wrap the tree
Section titled “2. Wrap the tree”import { createRuntime } from '@triggery/core';
import { TriggerRuntimeProvider } from '@triggery/solid';
import { render } from 'solid-js/web';
import { App } from './App';
const runtime = createRuntime();
render(
() => (
<TriggerRuntimeProvider runtime={runtime}>
<App />
</TriggerRuntimeProvider>
),
document.getElementById('root')!,
);3. Wire components
Section titled “3. Wire components”import { createSignal, type Component } from 'solid-js';
import { useAction, useCondition, useEvent } from '@triggery/solid';
import { messageTrigger } from './triggers/message.trigger';
const SettingsProvider: Component = () => {
const [settings] = createSignal({ sound: true, notifications: true });
useCondition(messageTrigger, 'settings', () => settings());
return null;
};
const Chat: Component = () => {
const fire = useEvent(messageTrigger, 'new-message');
return (
<button onClick={() => fire({ text: 'hi', author: 'Alice' })}>
send
</button>
);
};
const Toast: Component = () => {
useAction(messageTrigger, 'showToast', ({ title, body }) =>
console.log(`[${title}] ${body}`),
);
return null;
};
export const App: Component = () => (
<>
<SettingsProvider />
<Chat />
<Toast />
</>
);Click the button — Toast logs. Toggle settings.notifications to false — silent.
How Solid integration differs from React
Section titled “How Solid integration differs from React”Solid components only run their setup once. No useCallback, no dependency arrays, no ref dance — just close over what you need. Condition getters that read signals automatically see the latest value at fire time because Solid signals are pure functions.
// Compare to React: no deps array, no useCallback, no memoization.
useCondition(trigger, 'settings', () => settings());Cleanup
Section titled “Cleanup”All composables auto-clean via onCleanup — they work both inside component setup and inside detached createRoot() scopes. No manual disposer plumbing.