DEV Community

Cover image for Build a Realtime Chat App in 5 Minutes with PocketBase Cloud + React
tienbuilds
tienbuilds

Posted on

Build a Realtime Chat App in 5 Minutes with PocketBase Cloud + React

Realtime features usually mean one of two things: paying for Firebase, or wrestling with WebSocket servers at 2 AM. Today we're doing neither.

We'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.

Total time: about 5 minutes. Let's go. ⏱️

What we're building

  • A public chat room (no auth, to keep this short)
  • Messages stored in PocketBase (SQLite under the hood)
  • Realtime updates via SSE — open two tabs and watch them sync

Step 1 — Deploy PocketBase (30 seconds, literally)

  1. Go to pocketbasecloud.com and sign up (Free plan works fine for this tutorial)
  2. Click Deploy, pick a region close to you (there are 6 — Germany, Finland, US East/West, Singapore...)
  3. Wait ~30 seconds. You'll get a URL like:
https://your-app.pocketbasecloud.com
Enter fullscreen mode Exit fullscreen mode

That's a full PocketBase instance with SSL already configured. Open /_/ on that URL to access the admin dashboard and create your admin account:

https://your-app.pocketbasecloud.com/_/
Enter fullscreen mode Exit fullscreen mode

Self-hosting fans: everything below works identically on a self-hosted instance. The cloud just skips the VPS + nginx + certbot ritual.

Step 2 — Create the messages collection

In the admin dashboard:

  1. Collections → New collection → name it messages (type: Base)
  2. Add two fields:
    • username — Plain text, required
    • text — Plain text, required
  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.

⚠️ 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 != "".

That's the entire backend. No migrations, no ORM, no REST controllers.

Step 3 — Scaffold the React app

npm create vite@latest pb-chat -- --template react
cd pb-chat
npm install pocketbase
npm run dev
Enter fullscreen mode Exit fullscreen mode

Step 4 — The realtime magic

Replace src/App.jsx with this (~60 lines, that's the whole app):

import { useEffect, useRef, useState } from "react";
import PocketBase from "pocketbase";

// 👇 your PocketBase Cloud URL
const pb = new PocketBase("https://your-app.pocketbasecloud.com");

export default function App() {
  const [messages, setMessages] = useState([]);
  const [text, setText] = useState("");
  const [username] = useState(
    () => "guest-" + Math.random().toString(36).slice(2, 7)
  );
  const bottomRef = useRef(null);

  useEffect(() => {
    // 1. Load the latest 50 messages
    pb.collection("messages")
      .getList(1, 50, { sort: "created" })
      .then((res) => setMessages(res.items));

    // 2. Subscribe to realtime changes (SSE under the hood)
    pb.collection("messages").subscribe("*", (e) => {
      if (e.action === "create") {
        setMessages((prev) => [...prev, e.record]);
      }
    });

    // 3. Clean up the subscription on unmount
    return () => pb.collection("messages").unsubscribe("*");
  }, []);

  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: "smooth" });
  }, [messages]);

  async function send(e) {
    e.preventDefault();
    if (!text.trim()) return;
    await pb.collection("messages").create({ username, text });
    setText(""); // no manual state update needed — SSE delivers it back
  }

  return (
    <div style={{ maxWidth: 480, margin: "40px auto", fontFamily: "sans-serif" }}>
      <h2>⚡ PB Cloud Chat — you are {username}</h2>
      <div style={{ height: 360, overflowY: "auto", border: "1px solid #ddd", padding: 12, borderRadius: 8 }}>
        {messages.map((m) => (
          <p key={m.id}>
            <b>{m.username}:</b> {m.text}
          </p>
        ))}
        <div ref={bottomRef} />
      </div>
      <form onSubmit={send} style={{ display: "flex", gap: 8, marginTop: 12 }}>
        <input
          value={text}
          onChange={(e) => setText(e.target.value)}
          placeholder="Type a message…"
          style={{ flex: 1, padding: 8 }}
        />
        <button>Send</button>
      </form>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Run it, open two browser tabs, and send a message. Both tabs update instantly. 🎉

Notice 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.

What's actually happening under the hood?

PocketBase's realtime API uses Server-Sent Events, not WebSockets:

  1. The SDK opens a long-lived HTTP connection to /api/realtime
  2. It submits which collections/records you want to watch
  3. The server pushes JSON events (create / update / delete) down that stream

SSE 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.

Step 5 — Ship it

Build the frontend:

npm run build
Enter fullscreen mode Exit fullscreen mode

PocketBase 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.

Where to go from here

  • 🔐 Add auth: PocketBase ships with email/password + OAuth2 (Google, GitHub...) built in — then lock the Create rule to @request.auth.id != ""
  • 🗑️ Handle update/delete events in the subscription for message editing
  • 📄 Paginate history with getList(page, perPage)
  • 🚀 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

Try it here 👉 pocketbasecloud.com — the Free plan is genuinely 30 seconds to a live backend.

If you build something with this, drop a link in the comments. I'd love to see it! 💬

Top comments (0)