Skip to content

Event Bus Utility (defineBus)

This utility creates a type-safe global event bus using mitt, allowing components or services in your app to communicate via events without needing direct references to each other.

✨ Features

  • Type-safe events using TypeScript generics.
  • Shared or isolated buses by name.
  • Lightweight mitt.

🧠 Why

Event buses can reduce tight coupling and simplify communication across your app. By wrapping mitt, we ensure:

  • Proper deduplication using a name-based cache (Map<string, Emitter>)
  • Reusability: multiple calls to defineBus("myBus") return the same bus instance
  • Type safety for events

🚀 Usage

1. Basic Usage

Global Bus

ts
const { $bus } = defineBus()

$bus.on("hello", (msg) => console.log(msg))
$bus.emit("hello", "world")

Local Bus

These can be great to expose for domain apis, keeping our code modules decoupled.

ts
const { $bus: myBus } = defineBus("myBus")

myBus.on("hello", (msg) => console.log(msg))
myBus.emit("hello", "world")

2. Typed Events

ts
interface MyEvents {
    login: { userId: string }
    logout: void
}

const { $bus } = defineBus<MyEvents>("auth")

$bus.on("login", ({ userId }) => console.log("Logged in:", userId))
$bus.emit("login", { userId: "abc123" })

🛠 API

defineBus<TEvents>(name?: string)

Creates or returns a cached event bus.

Parameters:

  • TEvents: A type map of events (Record<EventType, any>)
  • name: Optional. Unique identifier for your bus. Defaults to "."

Returns:

ts
{
    $bus: Emitter<TEvents>
}

📦 Internals

  • Internally memoized via Map<string, Emitter> to avoid re-instantiation.

  • Events are emitted and listened to using the same API as mitt:

    • .on(event, handler)
    • .off(event, handler)
    • .emit(event, data)

🧼 Cleanup

Always .off() handlers when done to avoid memory leaks:

ts
const handler = (data) => {}
$bus.on("something", handler)
$bus.off("something", handler)

🧪 Testing Tip

In unit tests, call busses.clear() manually to reset state if needed.

Pro tip: Name your buses for scoped communication. No one likes an app where all events are global free-for-alls 😅