eventemitter3 vs events vs mitt vs pubsub-js
Client-Side Event Handling and PubSub Patterns
eventemitter3eventsmittpubsub-jsSimilar Packages:

Client-Side Event Handling and PubSub Patterns

eventemitter3, events, mitt, and pubsub-js are all libraries designed to handle event-driven architecture in JavaScript applications. They allow components to communicate without direct dependencies by subscribing to events and publishing data when changes occur. eventemitter3 and events follow the Node.js EventEmitter pattern, offering features like context binding and listener management. mitt is a minimalistic alternative focused on small bundle size, while pubsub-js implements a classic Publish-Subscribe pattern with token-based unsubscription. Choosing the right one depends on your need for Node.js compatibility, bundle size constraints, and specific API preferences like context binding or wildcard events.

Npm Package Weekly Downloads Trend

3 Years

Github Stars Ranking

Stat Detail

Package
Downloads
Stars
Size
Issues
Publish
License
eventemitter303,52874.4 kB214 months agoMIT
events01,409-135 years agoMIT
mitt011,87226.4 kB263 years agoMIT
pubsub-js04,863113 kB282 years agoMIT

Event Emitters vs PubSub: eventemitter3, events, mitt, and pubsub-js Compared

When building interactive web applications, components often need to talk to each other without being tightly coupled. Event emitters and PubSub libraries solve this by allowing you to subscribe to messages and publish them when needed. While eventemitter3, events, mitt, and pubsub-js all handle this core task, they differ in API design, features, and environment optimization. Let's break down how they handle common engineering scenarios.

πŸ› οΈ Initializing and Emitting Events

The first thing you notice is how you create an instance and send messages. eventemitter3 and events use a class-based approach where you instantiate an emitter object. mitt is similar but lighter. pubsub-js uses a static global object by default, removing the need to manage an instance.

eventemitter3 requires you to create an instance.

import EventEmitter from 'eventemitter3';
const emitter = new EventEmitter();
emitter.emit('update', { id: 1 });

events also uses a class instance, mirroring Node.js core.

import EventEmitter from 'events';
const emitter = new EventEmitter();
emitter.emit('update', { id: 1 });

mitt creates a lightweight instance.

import mitt from 'mitt';
const emitter = mitt();
emitter.emit('update', { id: 1 });

pubsub-js uses a static global API.

import PubSub from 'pubsub-js';
// No instance needed
PubSub.publish('update', { id: 1 });

πŸ”Œ Subscribing and Unsubscribing

Managing subscriptions is critical to prevent memory leaks. The way you remove listeners varies significantly. eventemitter3, events, and mitt require you to pass the same function reference to stop listening. pubsub-js returns a token when you subscribe, which you use to unsubscribe later.

eventemitter3 needs the original handler function.

const handler = (data) => console.log(data);
emitter.on('update', handler);
// Later
emitter.off('update', handler);

events works the same way, using off or removeListener.

const handler = (data) => console.log(data);
emitter.on('update', handler);
// Later
emitter.off('update', handler);

mitt also requires the handler reference.

const handler = (data) => console.log(data);
emitter.on('update', handler);
// Later
emitter.off('update', handler);

pubsub-js uses a subscription token.

const token = PubSub.subscribe('update', (msg, data) => console.log(data));
// Later
PubSub.unsubscribe(token);

🧭 Handling Context (this)

In object-oriented code, you often need this to refer to your class instance inside a callback. eventemitter3 and events allow you to bind context directly in the subscription method. mitt and pubsub-js do not support this natively, requiring you to use .bind() or arrow functions manually.

eventemitter3 accepts a context argument.

class Component {
  constructor() {
    emitter.on('update', this.handleUpdate, this);
  }
  handleUpdate(data) { /* this refers to Component */ }
}

events does not have a built-in context argument in the standard API.

class Component {
  constructor() {
    // Must bind manually
    emitter.on('update', this.handleUpdate.bind(this));
  }
  handleUpdate(data) { /* this refers to Component */ }
}

mitt has no context support.

class Component {
  constructor() {
    // Must bind manually
    emitter.on('update', this.handleUpdate.bind(this));
  }
  handleUpdate(data) { /* this refers to Component */ }
}

pubsub-js has no context support.

class Component {
  constructor() {
    // Must bind manually
    PubSub.subscribe('update', this.handleUpdate.bind(this));
  }
  handleUpdate(msg, data) { /* this refers to Component */ }
}

⏱️ One-Time Listeners

Sometimes you only want to listen for an event once, like initializing a resource. eventemitter3 and events provide a once method. mitt and pubsub-js do not have this built-in, so you must manually unsubscribe after the first call.

eventemitter3 has a dedicated once method.

emitter.once('ready', () => {
  console.log('Ready only once');
});

events also includes once.

emitter.once('ready', () => {
  console.log('Ready only once');
});

mitt requires manual removal.

const handler = () => {
  console.log('Ready only once');
  emitter.off('ready', handler);
};
emitter.on('ready', handler);

pubsub-js requires manual removal.

const token = PubSub.subscribe('ready', (msg) => {
  console.log('Ready only once');
  PubSub.unsubscribe(token);
});

🌐 Wildcard Support

For advanced debugging or global logging, you might want to listen to all events at once. eventemitter3 supports wildcard listeners using *. The other three libraries do not support this feature out of the box.

eventemitter3 supports wildcards.

emitter.on('*', (event, data) => {
  console.log(`All events: ${event}`, data);
});

events does not support wildcards.

// No native wildcard support
// You would need to wrap the emitter or manually emit to a log topic

mitt does not support wildcards.

// No native wildcard support
// You would need to manually emit to a log topic

pubsub-js does not support wildcards.

// No native wildcard support
// You would need to manually publish to a log topic

πŸ“Š Summary: Key Differences

Featureeventemitter3eventsmittpubsub-js
API StyleInstance-basedInstance-basedInstance-basedStatic Global
UnsubscribePass handlerPass handlerPass handlerUse token
ContextBuilt-in supportManual bindManual bindManual bind
OnceBuilt-in onceBuilt-in onceManualManual
Wildcardsβœ… Supported❌ No❌ No❌ No
Primary UseFrontend AppsIsomorphic/NodeTiny BundlesDecoupled Systems

πŸ’‘ The Big Picture

eventemitter3 is the strongest all-rounder for frontend development. It gives you the powerful Node.js API you might already know, but it strips away the heavy Node.js dependencies. The built-in context binding and wildcard support save you from writing boilerplate code.

events is essential if you are writing a library that must run in Node.js and the browser without changes. It ensures compatibility with the Node.js ecosystem, but it can be heavier than needed for simple browser tasks.

mitt is the choice for performance-critical applications where bundle size is the main concern. It trades features for speed and size. If you just need to pass messages and don't need this binding or once, it is a great fit.

pubsub-js offers a different mental model with token-based unsubscription. This can be cleaner in some architectures where you don't want to keep references to your handler functions around just to clean them up later.

Final Thought: For most modern React or Vue applications, eventemitter3 offers the best balance of features and performance. If you are building a tiny widget, grab mitt. If you are polyfilling Node core, stick with events.

How to Choose: eventemitter3 vs events vs mitt vs pubsub-js

  • eventemitter3:

    Choose eventemitter3 if you want the familiar Node.js EventEmitter API but optimized for the browser. It supports context binding, wildcards, and has a smaller footprint than the full events polyfill. It is ideal for React applications or complex frontend state management where you need robust listener control without Node.js dependencies.

  • events:

    Choose events if you are building isomorphic libraries that must run identically in Node.js and the browser without modification. It is the exact polyfill for the Node.js core module. Use this if you depend on other Node.js streams or modules that explicitly require the native EventEmitter interface.

  • mitt:

    Choose mitt if bundle size is your top priority and you need a simple global event bus. It lacks advanced features like context binding or once listeners, making it best for straightforward publish-subscribe needs in small projects or micro-frontends where every kilobyte counts.

  • pubsub-js:

    Choose pubsub-js if you prefer a decoupled PubSub pattern where unsubscription is handled via tokens rather than passing the original callback. It is well-suited for legacy codebases or architectures where you want to avoid holding references to handler functions for cleanup.

README for eventemitter3

EventEmitter3

Version npmCICoverage Status

EventEmitter3 is a high performance EventEmitter. It has been micro-optimized for various of code paths making this, one of, if not the fastest EventEmitter available for Node.js and browsers. The module is API compatible with the EventEmitter that ships by default with Node.js but there are some slight differences:

  • Domain support has been removed.
  • We do not throw an error when you emit an error event and nobody is listening.
  • The newListener and removeListener events have been removed as they are useful only in some uncommon use-cases.
  • The setMaxListeners, getMaxListeners, prependListener and prependOnceListener methods are not available.
  • Support for custom context for events so there is no need to use fn.bind.
  • The removeListener method removes all matching listeners, not only the first.

It's a drop in replacement for existing EventEmitters, but just faster. Free performance, who wouldn't want that? The EventEmitter is written in EcmaScript 3 so it will work in the oldest browsers and node versions that you need to support.

Installation

$ npm install --save eventemitter3

CDN

Recommended CDN:

https://unpkg.com/eventemitter3@latest/dist/eventemitter3.umd.min.js

Usage

After installation the only thing you need to do is require the module:

var EventEmitter = require('eventemitter3');

And you're ready to create your own EventEmitter instances. For the API documentation, please follow the official Node.js documentation:

http://nodejs.org/api/events.html

Contextual emits

We've upgraded the API of the EventEmitter.on, EventEmitter.once and EventEmitter.removeListener to accept an extra argument which is the context or this value that should be set for the emitted events. This means you no longer have the overhead of an event that required fn.bind in order to get a custom this value.

var EE = new EventEmitter()
  , context = { foo: 'bar' };

function emitted() {
  console.log(this === context); // true
}

EE.once('event-name', emitted, context);
EE.on('another-event', emitted, context);
EE.removeListener('another-event', emitted, context);

Tests and benchmarks

To run tests run npm test. To run the benchmarks run npm run benchmark.

Tests and benchmarks are not included in the npm package. If you want to play with them you have to clone the GitHub repository. Note that you will have to run an additional npm i in the benchmarks folder before npm run benchmark.

License

MIT