DEV Community

Zephyr Events – A 2KB TypeScript event emitter that's race-condition safe

I built a tiny event emitter that fixes a bug most people don't know they have: if a handler calls off() on itself during emit, the next handler gets skipped. EventEmitter3, Node's built-in EventEmitter, and mitt all have this problem.

Zephyr Events uses snapshot-based iteration so handlers can subscribe, unsubscribe, or clear listeners mid-emit without side effects. If you don't need that safety, there's a zephyrEventsFast mode that's up to 82% faster.

1.9KB, zero dependencies, tree-shakeable
Full TypeScript generics with strict handler signatures
Wildcard listeners, shared handler maps, unsubscribe functions
ESM, CommonJS, and UMD builds

import zephyrEvents from 'zephyr-events';

type Events = {
  'user:login': { id: number; name: string }
  'error': Error
}

const emitter = zephyrEvents<Events>();
const unsub = emitter.on('user:login', (user) => {
  console.log(`Welcome, ${user.name}`);
});
emitter.emit('user:login', { id: 1, name: 'Alice' });
unsub();
Enter fullscreen mode Exit fullscreen mode

https://www.npmjs.com/package/zephyr-events
Would love feedback, especially on the API design and whether the safe/fast split makes sense as a default.

Top comments (0)