https://github.com/jordan-sussman/phoenix-presence-react
A lightweight React hook for real-time presence tracking with Elixir Phoenix Channels
https://github.com/jordan-sussman/phoenix-presence-react
elixir elixir-phoenix phoenix presence react react-hooks real-time
Last synced: 25 days ago
JSON representation
A lightweight React hook for real-time presence tracking with Elixir Phoenix Channels
- Host: GitHub
- URL: https://github.com/jordan-sussman/phoenix-presence-react
- Owner: jordan-sussman
- License: mit
- Created: 2026-05-24T18:39:22.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-05-24T19:05:47.000Z (about 1 month ago)
- Last Synced: 2026-05-24T20:26:14.200Z (about 1 month ago)
- Topics: elixir, elixir-phoenix, phoenix, presence, react, react-hooks, real-time
- Language: TypeScript
- Homepage:
- Size: 132 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# phoenix-presence-react
React hook for [Phoenix Presence](https://hexdocs.pm/phoenix/Phoenix.Presence.html). Tracks who's online in real time via Phoenix channels.
Screenshot from example app:

## Install
```bash
npm install phoenix-presence-react
```
## Usage
```tsx
import { usePresence } from "phoenix-presence-react";
function OnlineUsers({
userId,
userName,
}: {
userId: string;
userName: string;
}) {
const { presence, isJoined } = usePresence({
topic: "presence:lobby",
params: { token: "user-token" }, // params passed to your Elixir channel's join/3
});
return (
{presence.map((user) => (
))}
);
}
```
### Build
To compile the library:
```bash
npm install
npm run build
```
### Running the Example
The example uses a `mock-phoenix` implementation to simulate presence events without requiring a live Elixir backend.
To run the provided example app from screenshot:
```bash
cd example
npm install
npm run dev
```
## Advanced Usage
### Typed Metadata
```tsx
interface MyMeta {
name: string;
avatar_url: string;
}
// 'presence' will be typed as (MyMeta & { id: string })[]
const { presence } = usePresence({ topic: "room" });
```
### Callbacks for side effects
```tsx
usePresence({
topic: "room",
onJoin: (id, current, newPres) => {
console.log(`${newPres.metas[0].name} joined!`);
},
onLeave: (id, current, leftPres) => {
console.log(`${leftPres.metas[0].name} left!`);
},
});
```
### Access raw Presence state
```tsx
const { state } = usePresence({ topic: "room" });
/*
state is the raw Phoenix Record:
{
"user-1": { metas: [{ name: "Alice", phx_ref: "..." }] },
"user-2": { metas: [{ name: "Bob", phx_ref: "..." }] }
}
*/
```
## Options
```ts
usePresence({
topic: "presence:lobby", // Default: "presence:lobby"
params: { token: "..." }, // Params for socket/channel
socketUrl: "/socket", // Default: "/socket" (only used if no socket/channel provided)
socket: externalSocket, // Optional: provide existing Phoenix Socket
channel: externalChannel, // Optional: provide existing Phoenix Channel
});
```
## Return value
```ts
{
presence: (PresenceMeta & { id: string })[], // Flattened list of online users
state: Record, // Raw Phoenix presence state
isJoined: boolean, // Channel join status
error: Error | null, // Join error
}
```
## Required Elixir backend
You need a Phoenix channel using `Phoenix.Presence`.
### 1. Define your Presence module
```elixir
defmodule MyAppWeb.Presence do
use Phoenix.Presence,
otp_app: :my_app,
pubsub_server: MyApp.PubSub
end
```
### 2. Update your Channel
```elixir
defmodule MyAppWeb.PresenceChannel do
use MyAppWeb, :channel
alias MyAppWeb.Presence
def join("presence:lobby", _params, socket) do
send(self(), :after_join)
{:ok, socket}
end
def handle_info(:after_join, socket) do
{:ok, _} = Presence.track(socket, socket.assigns.user_id, %{
online_at: System.os_time(:second),
name: socket.assigns.user_name
})
push(socket, "presence_state", Presence.list(socket))
{:noreply, socket}
end
end
```