Realtime features you copy into your app. Notifications, chat, presence, AI agent streaming and voice, for Node, Python and Go servers, with React UI that works with any of them.
Documentation · Browse the kits · Build one in 15 minutes · For AI assistants: llms.txt
Notifications, chat with replayed history, a presence roster, an agent streaming tokens to the browser: add the one you need, in a single command, and own the code it copies in. Inspired by shadcn/ui, but built for WebSocket applications.
uvx copit add @chanx-kit/notification- The code is yours. A kit lands in your repository as ordinary classes you can read, subclass, or edit outright. No hidden layer, and no waiting for someone to add the hook you need.
- Only the part you need. Install one kit, not a framework. Your footprint is exactly what you used, and nothing is pulled in behind it.
- Django and FastAPI, same kit. Kits import only framework-agnostic chanx, so the same component runs on Django Channels and on FastAPI / fast-channels.
- Go and Node too. Notification, presence, room chat and AG-UI are ported to chanx-go and chanx-js, with the same messages and topic names, checked in CI. The UI kits work against any of them, and the servers can share one Redis.
- Production-shaped, not demo-shaped. Authorization per subscription, pluggable persistence, and READMEs that say plainly which defaults are single-process. Every kit is tested on both backends and type-checked in strict mode.
- Composable. Kits are listed on a consumer rather than inherited, so any number of them work side by side, and each one still works alone.
- UI kits too. The browser half, React first, installed the same way. Each binds to the messages a kit speaks rather than to one server kit, so one UI works with every provider. They compose like server kits: hooks on one channel share one socket, and the voice agent UI is the transcriber, player and AG-UI kits put together.
Use cases:
- AG-UI agents, and bi-directional agent streaming
- Notifications
- Room chat with replayed history and live presence
- Voice: live captions, text to speech, and a voice agent you can interrupt, with Deepgram, ElevenLabs or OpenAI
- And more ...
Install copit, the CLI that copies starters and
kits in (also on npm as @copit/cli and on PyPI as copit):
curl -fsSL https://raw.githubusercontent.com/huynguyengl99/copit/main/install.sh | bashThen create a server and a React client with the notification kit wired end to end, the client typed from the server's schema:
copit create gh:chanx-dev/chanx-kit@v0.5.1/templates/express-react my-appSwap express-react for your framework: fastify-react, hono-react,
fastapi-react, django-react, go-nethttp-react, go-chi-react, go-gin-react,
go-echo-react or go-fiber-react. The project comes with its own copit.toml, so
adding kits works from the first cd, and its README has the commands to run it.
To add kits to a project you already have, install chanx, if you have not yet. It is the only package a kit needs:
pip install "chanx[fast_channels]" # FastAPI and other ASGI apps
pip install "chanx[channels]" # DjangoPoint copit at this registry, once. It
records where kits come from and where they land, in copit.toml:
uvx copit init
uvx copit registry add chanx-kit github:chanx-dev/chanx-kit@v0.5.1 --to app/ws_kitsAdd a kit. The source is copied into app/ws_kits/notification/, along with any kit
it depends on:
uvx copit add @chanx-kit/notificationList it on a consumer. A kit is a topic: a pattern clients address, with its own handlers, authorization and group.
class AppConsumer(AsyncJsonWebsocketConsumer[NotificationMessage]):
channel_layer_alias = "default"
topics = [UserNotificationTopic, BroadcastNotificationTopic]Use it from anywhere. A signal, a worker or another service can reach connected clients without importing a consumer:
await UserNotificationTopic.notify_user(
user.id, NotificationPayload(title="Build finished", level="success")
)From here, the getting started guide covers subscribing from a client, composing several kits, and overriding a kit's hooks.
One command per feature, server and browser halves together. Set up the UI registry once as well (UI kits).
| Feature | Install |
|---|---|
| Notifications | copit add @chanx-kit/notification @chanx-kit-ui/notification |
| Chat room with a roster | copit add @chanx-kit/room-chat @chanx-kit-ui/chat |
| Presence | copit add @chanx-kit/presence @chanx-kit-ui/presence |
| Agent streaming over AG-UI | copit add @chanx-kit/ag-ui @chanx-kit-ui/ag-ui |
| AI agent over AG-UI | copit add @chanx-kit/pydantic-ai-ag-ui @chanx-kit-ui/ag-ui |
| Live captions | copit add @chanx-kit/deepgram @chanx-kit-ui/transcriber --only stt |
| Text to speech | copit add @chanx-kit/elevenlabs @chanx-kit-ui/player --only tts |
| Voice agent | copit add @chanx-kit/voice-agent @chanx-kit/deepgram @chanx-kit-ui/voice-agent |
Server kits work without the UI ones, and the other way round: a UI kit binds to the
messages a server kit speaks, so any provider (deepgram, elevenlabs, openai or the
keyless fake-voice) fits the same UI. Mixing registries with --only needs copit
0.9.1 or later.
Stores, a Redis-backed roster, a Django-backed message store and the test harness are kits too. See python/, go/, js/ and ui/ in this repository, or browse them all with their messages, hooks and caveats.
uv sync
npm --prefix ui/sandbox install && npm --prefix ui/sandbox run build
uv run python -m sandboxOpen http://localhost:8000: a sidebar of demos grouped like the docs, each naming the kits and consumer it runs, plus AsyncAPI docs for the generated WebSocket API. Open the chat room in a second tab to watch history replay and the roster update live.
The same UI runs on the Go and Node kits too: go -C go/sandbox run . -framework gin or
npm --prefix js run sandbox -- --framework hono, any of their frameworks.
Realtime features look different but are built from the same plumbing: group naming, authorization per subscriber, state on connect, cleanup on disconnect, and a way for a worker or a signal to reach a live client. Most of us write it again in every project, or copy it from a tutorial that never mentions what breaks once you run two processes.
The usual escape is a package that does it for you and then owns your consumers. The day you need to change how a room is authorized, you are reading someone else's source looking for a hook they did not add. Copying the component in avoids both: you start from something that already works, and it is yours from the first line.
New kits are welcome, especially ones you already run yourself. See CONTRIBUTING.md for the checklist: a kit needs metadata, a README, and tests that pass on both backends.
MIT