{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreiahquty6r5oma4e4si6kh3peun2flhddvkd75cisw2lzo3bm5kr5y",
"uri": "at://did:plc:25rdn5elo5izoxrmtis34zuk/app.bsky.feed.post/3mpq3ca26iex2"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreieot2j46chodhc7fb2kw4vghzxi2ybztq5tcwfxabwmygvpr3kkpe"
},
"mimeType": "image/webp",
"size": 97798
},
"path": "/tienbuilds/build-a-realtime-chat-app-in-5-minutes-with-pocketbase-cloud-react-172e",
"publishedAt": "2026-07-03T07:42:28.000Z",
"site": "https://dev.to",
"tags": [
"react",
"pocketbase",
"tutorial",
"webdev",
"PocketBase Cloud",
"pocketbasecloud.com",
"@request.auth.id"
],
"textContent": "Realtime features usually mean one of two things: paying for Firebase, or wrestling with WebSocket servers at 2 AM. Today we're doing neither.\n\nWe're going to build a **live chat app** where messages appear instantly across all connected browsers — using PocketBase Cloud for the backend and React for the frontend. No servers to configure, no WebSocket code to write. PocketBase handles realtime over **Server-Sent Events (SSE)** , and the JS SDK wraps it in one function call.\n\nTotal time: about 5 minutes. Let's go. ⏱️\n\n## What we're building\n\n * A public chat room (no auth, to keep this short)\n * Messages stored in PocketBase (SQLite under the hood)\n * Realtime updates via SSE — open two tabs and watch them sync\n\n\n\n## Step 1 — Deploy PocketBase (30 seconds, literally)\n\n 1. Go to **pocketbasecloud.com** and sign up (Free plan works fine for this tutorial)\n 2. Click **Deploy** , pick a region close to you (there are 6 — Germany, Finland, US East/West, Singapore...)\n 3. Wait ~30 seconds. You'll get a URL like:\n\n\n\n\n https://your-app.pocketbasecloud.com\n\n\nThat's a full PocketBase instance with SSL already configured. Open `/_/` on that URL to access the admin dashboard and create your admin account:\n\n\n\n https://your-app.pocketbasecloud.com/_/\n\n\n> Self-hosting fans: everything below works identically on a self-hosted instance. The cloud just skips the VPS + nginx + certbot ritual.\n\n## Step 2 — Create the `messages` collection\n\nIn the admin dashboard:\n\n 1. **Collections → New collection** → name it `messages` (type: Base)\n 2. Add two fields:\n * `username` — Plain text, required\n * `text` — Plain text, required\n 3. Open the **API Rules** tab and set both **List/Search rule** and **View rule** and **Create rule** to empty (unlock them) so anyone can read and post.\n\n\n\n⚠️ Empty rules = public access. Fine for a demo chat; for a real app you'd lock Create to authenticated users with a rule like `@request.auth.id != \"\"`.\n\nThat's the entire backend. No migrations, no ORM, no REST controllers.\n\n## Step 3 — Scaffold the React app\n\n\n npm create vite@latest pb-chat -- --template react\n cd pb-chat\n npm install pocketbase\n npm run dev\n\n\n## Step 4 — The realtime magic\n\nReplace `src/App.jsx` with this (~60 lines, that's the whole app):\n\n\n\n import { useEffect, useRef, useState } from \"react\";\n import PocketBase from \"pocketbase\";\n\n // 👇 your PocketBase Cloud URL\n const pb = new PocketBase(\"https://your-app.pocketbasecloud.com\");\n\n export default function App() {\n const [messages, setMessages] = useState([]);\n const [text, setText] = useState(\"\");\n const [username] = useState(\n () => \"guest-\" + Math.random().toString(36).slice(2, 7)\n );\n const bottomRef = useRef(null);\n\n useEffect(() => {\n // 1. Load the latest 50 messages\n pb.collection(\"messages\")\n .getList(1, 50, { sort: \"created\" })\n .then((res) => setMessages(res.items));\n\n // 2. Subscribe to realtime changes (SSE under the hood)\n pb.collection(\"messages\").subscribe(\"*\", (e) => {\n if (e.action === \"create\") {\n setMessages((prev) => [...prev, e.record]);\n }\n });\n\n // 3. Clean up the subscription on unmount\n return () => pb.collection(\"messages\").unsubscribe(\"*\");\n }, []);\n\n useEffect(() => {\n bottomRef.current?.scrollIntoView({ behavior: \"smooth\" });\n }, [messages]);\n\n async function send(e) {\n e.preventDefault();\n if (!text.trim()) return;\n await pb.collection(\"messages\").create({ username, text });\n setText(\"\"); // no manual state update needed — SSE delivers it back\n }\n\n return (\n <div style={{ maxWidth: 480, margin: \"40px auto\", fontFamily: \"sans-serif\" }}>\n <h2>⚡ PB Cloud Chat — you are {username}</h2>\n <div style={{ height: 360, overflowY: \"auto\", border: \"1px solid #ddd\", padding: 12, borderRadius: 8 }}>\n {messages.map((m) => (\n <p key={m.id}>\n <b>{m.username}:</b> {m.text}\n </p>\n ))}\n <div ref={bottomRef} />\n </div>\n <form onSubmit={send} style={{ display: \"flex\", gap: 8, marginTop: 12 }}>\n <input\n value={text}\n onChange={(e) => setText(e.target.value)}\n placeholder=\"Type a message…\"\n style={{ flex: 1, padding: 8 }}\n />\n <button>Send</button>\n </form>\n </div>\n );\n }\n\n\nRun it, open **two browser tabs** , and send a message. Both tabs update instantly. 🎉\n\nNotice the neat trick in `send()`: we don't append the message to local state ourselves. We just `create()` the record, and the realtime subscription delivers it back to _every_ client — including the sender. One source of truth, zero sync bugs.\n\n## What's actually happening under the hood?\n\nPocketBase's realtime API uses **Server-Sent Events** , not WebSockets:\n\n 1. The SDK opens a long-lived HTTP connection to `/api/realtime`\n 2. It submits which collections/records you want to watch\n 3. The server pushes JSON events (`create` / `update` / `delete`) down that stream\n\n\n\nSSE is simpler than WebSockets (plain HTTP, auto-reconnect built into the browser) and passes through proxies and firewalls more reliably. For server→client pushes like chat, feeds, and dashboards, it's honestly all you need.\n\n## Step 5 — Ship it\n\nBuild the frontend:\n\n\n\n npm run build\n\n\nPocketBase Cloud includes **frontend hosting** on every plan (even Free), so you can deploy the `dist/` folder right next to your backend — one dashboard, one URL, no CORS headaches. Or push it to any static host you like; the backend URL is public either way.\n\n## Where to go from here\n\n * 🔐 Add auth: PocketBase ships with email/password + OAuth2 (Google, GitHub...) built in — then lock the Create rule to `@request.auth.id != \"\"`\n * 🗑️ Handle `update`/`delete` events in the subscription for message editing\n * 📄 Paginate history with `getList(page, perPage)`\n * 🚀 Running multiple side projects? The Pro plan (from $13/mo) lets you run **unlimited PocketBase instances on one dedicated server** — many users fit 10–20 apps on it\n\n\n\nTry it here 👉 **pocketbasecloud.com** — the Free plan is genuinely 30 seconds to a live backend.\n\nIf you build something with this, drop a link in the comments. I'd love to see it! 💬",
"title": "Build a Realtime Chat App in 5 Minutes with PocketBase Cloud + React"
}