---
sidebar_position: 5
---

# Client events

Browsers on a private or presence channel can send each other events directly, with no request to your server: typing indicators, cursors, live drawing.

```js
const board = rt.subscribe( 'presence-board-1' );

board.on( 'client-typing', ( data, { user_id } ) => showTyping( user_id, data.on ) );

let typed = 0;
input.addEventListener( 'input', () => {
	if ( Date.now() - typed > 2000 ) {
		typed = Date.now();
		board.trigger( 'client-typing', { on: true } ).catch( ( e ) => console.warn( e.code ) );
	}
} );
```

- **Names start with `client-`.** Any other is refused with `bad_event`.
- **Only on private and presence channels** the tab has subscribed to. A public channel's are refused with `forbidden`, since anyone could listen and send.
- **Everyone else on the channel gets it,** the sender's other tabs included, but not the tab that sent it.
- **`user_id`** names the sender: the user the channel's token names, on a presence channel. Your server never sees client events, so treat them as what any member could send.
- **Up to 10 a second** a browser and 10 KB each. Past it, `trigger()` rejects with `rate_limited` or `too_large`.

`trigger()` returns a promise that resolves once the server has passed the event on, or rejects with an error whose `code` says why ([errors](errors.md)).
