https://github.com/forge-42/web-events
Framework-agnostic custom event-driven handling library for the web
https://github.com/forge-42/web-events
event event-driven event-management events react web
Last synced: 12 months ago
JSON representation
Framework-agnostic custom event-driven handling library for the web
- Host: GitHub
- URL: https://github.com/forge-42/web-events
- Owner: forge-42
- License: mit
- Created: 2025-06-16T12:43:41.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-06-18T13:10:39.000Z (about 1 year ago)
- Last Synced: 2025-06-18T13:11:43.489Z (about 1 year ago)
- Topics: event, event-driven, event-management, events, react, web
- Language: TypeScript
- Homepage:
- Size: 23.4 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.MD
Awesome Lists containing this project
README
# ๐ฆ @forge42/web-events
**A tiny web-standards based utility for event-driven web apps.**






Leverages [`@standard-schema/spec`](https://www.npmjs.com/package/@standard-schema/spec) to safely dispatch and listen to custom events in the browser OR the server by using the `EventTarget` interface.
## โจ Features
- ๐ก๏ธ **Zero dependencies** for a lean footprint
- โ
**Type-safe** event dispatching and listening
- ๐ **Runtime validation** powered by `@standard-schema/spec`
- ๐งช **Minimal, browser-only API**
- ๐ชถ **Lightweight** and framework-agnostic
---
## ๐ฆ Installation
```bash
npm install @forge42/web-events
# or
pnpm add @forge42/web-events
```
## ๐ Usage
### Core API
This works both on the client and server side, as it uses the `EventTarget` interface.
```ts
import { registerEvent } from "@forge42/web-events"
import { z } from "zod"
// Define your schema
const UserLoggedIn = z.object({
userId: z.string(),
timestamp: z.number(),
})
// Register event
const [dispatch, listener] = registerEvent("user:logged-in", UserLoggedIn)
// Listen for event
const unsubscribe = listener((data) => {
console.log("User logged in:", data.userId)
})
// Dispatch event
dispatch({
userId: "abc123",
timestamp: Date.now(),
})
```
The `registerEvent` function returns a tuple containing the `dispatch` function and the `listener` function. The `listener` can be used to subscribe to the event, and it returns an `unsubscribe` function to stop listening.
```ts
const [dispatch, listener] = registerEvent("user:logged-in", UserLoggedIn)
```
Once the event is registered, you can dispatch it with the expected data structure, and it will be validated against the schema.
```ts
// This will match the schema provided to the registerEvent function
dispatch({
userId: "abc123",
timestamp: Date.now(),
})
```
If you want to listen to the incoming events in your application, you can use the `listener` function. It accepts a callback that will be invoked with the validated data whenever the event is dispatched.
```ts
const unsubscribe = listener((data) => {
console.log("User logged in:", data.userId)
})
// Optionally you can unsubscribe later
unsubscribe()
```
You can also pass in a third optional parameter to the `registerEvent` function to specify the event init options, such as `bubbles`, `cancelable`, or `composed`. This allows you to control the behavior of the event in the DOM.
It also takes in an optional property `emitter` which can be used to specify the `EventTarget` that will emit the event. This is useful if you want to use a custom event target instead of the default one.
```ts
const [dispatch, listener] = registerEvent("user:logged-in", UserLoggedIn, {
bubbles: true,
composed: true,
emitter: document // or any other EventTarget
})
```
### React
You can also use `@forge42/web-events` in a React application. It comes with a React specific API that allows you to register and listen to events in a more React-friendly way.
```tsx
import { registerReactEvent } from "@forge42/web-events/react"
import { z } from "zod"
// Define your schema
const UserLoggedIn = z.object({
userId: z.string(),
timestamp: z.number(),
})
// Register event
const [dispatchUserLogin, useUserLoggedInEvent] = registerReactEvent("user:logged-in", UserLoggedIn)
// Use the event in a React component
export default function UserComponent() {
const userLoggedIn = useUserLoggedInEvent()
const handleLogin = () => dispatchUserLogin({
userId: "abc123",
timestamp: Date.now(),
})
return (
Log In
{userLoggedIn && User logged in: {userLoggedIn.userId}
}
)
}
```
The `registerReactEvent` function returns a tuple containing the `dispatch` function and a custom React hook (`useEventListener`) that you can use to access the event data in your components. The hook accepts
an object with an `onEvent` callback that will be called whenever the event is received.
```tsx
const [dispatch, useEventListener] = registerReactEvent("user:logged-in", UserLoggedIn)
// Use the event in a React component
export default function UserComponent() {
const userLoggedIn = useEventListener({
onEvent: (data) => {
console.log("User logged in:", data.userId)
},
})
const handleLogin = () => {
dispatch({
userId: "abc123",
timestamp: Date.now(),
})
}
return (
Log In
{userLoggedIn && User logged in: {userLoggedIn.userId}
}
)
}
```
#### `useEvent` hook
The `useEvent` hook is a custom React hook that allows you to listen to events in a more declarative way. It accepts an initialized `registerEvent` instance and returns the data.
```tsx
import { useEvent } from "@forge42/web-events/react"
const messageSentEvent = registerEvent("message:sent", z.object({
message: z.string(),
count: z.number().optional(),
date: z.date().optional(),
}));
export default function MessageComponent() {
const messageSent = useEvent(messageSentEvent, {
onEvent: (data) => {
console.log("Message sent:", data);
},
});
return (
{messageSent ? (
Message: {messageSent.message}, Count: {messageSent.count}, Date: {messageSent.date?.toISOString()}
) : (
No message sent yet.
)}
);
}
```
### Schema Validation
The library accepts any schema compatible with `@standard-schema/spec`, such as Zod, Yup, or Joi. This allows you to define the structure of your event data and ensures that only valid data is dispatched.
```ts
import { z } from "zod"
const UserLoggedIn = z.object({
userId: z.string(),
timestamp: z.number(),
})
```
### Framework Integration
You can easily integrate `@forge42/web-events` with popular frameworks like React, Vue, or Svelte. Here's an example for React:
```tsx
import React, { useEffect } from "react"
import { registerEvent } from "@forge42/web-events"
const UserLoggedIn = z.object({
userId: z.string(),
timestamp: z.number(),
})
// Do not initialize the event inside the component to avoid re-registering on every render
const [dispatch, listener] = registerEvent("user:logged-in", UserLoggedIn)
const UserComponent = () => {
useEffect(() => {
const unsubscribe = listener((data) => {
console.log("User logged in:", data.userId)
})
return () => {
unsubscribe() // Cleanup on unmount
}
}, [])
const handleLogin = () => {
dispatch({
userId: "abc123",
timestamp: Date.now(),
})
}
return Log In
}
export default UserComponent
```
## ๐ก๏ธ Type Safety
This utility uses your schema to infer:
dispatch(input) expects data that matches the input schema
listener(callback) receives validated and parsed data
## ๐๏ธ Example Use Case
Use it to:
Broadcast form submissions
Track user interactions across decoupled modules
Replace fragile string-based pub/sub logic with type-safe guarantees
## ๐ License
MIT License