ANTIGRAVITY LABJP
Articles/Agents & Manager
Agents & Manager/2026-03-24Intermediate

Build a Real-Time Chat App with Antigravity Agents and Firebase— A Hands-On Guide

A complete hands-on guide to building a real-time chat app with Firebase Realtime Database, authentication, and push notifications — powered by Antigravity's AI agents.

Antigravity338Firebase6RealtimeChat AppAgents22Premium2

Introduction: Why Antigravity × Firebase?

Real-time chat apps are among the most popular categories in mobile app development. However, they require managing WebSocket connections, authentication flows, push notifications, and data persistence — typically requiring weeks of development.

By combining Antigravity's AI agents with Firebase, you can semi-automate these complex implementations and build a production-grade real-time chat app in a fraction of the time. This guide walks you through the complete process, from project setup to Firebase Authentication, Realtime Database integration, and push notification implementation.

Who this guide is for:

  • Developers comfortable with Antigravity's basic operations
  • Anyone with Firebase experience or interest
  • Those looking to build real-time features efficiently

Setting Up the Firebase Project

First, create a Firebase project in the console and prepare it for connection from Antigravity.

Installing and Initializing Firebase CLI

# Install Firebase CLI
npm install -g firebase-tools
 
# Log in
firebase login
 
# Initialize project (select Realtime Database + Authentication + Cloud Messaging)
firebase init
 
# Expected output:
# ✔ Firebase initialization complete!
# - database: Firebase Realtime Database Rules
# - auth: Firebase Authentication

Configuring Firebase in Your Antigravity Project

Add context to your agents.md file so Antigravity's agents understand your Firebase setup:

<!-- agents.md -->
# Firebase Chat App Agent
 
## Context
- Firebase project: my-chat-app-2026
- Realtime Database URL: https://my-chat-app-2026-default-rtdb.firebaseio.com
- Auth methods: Email/Password + Google Sign-In
- Target platform: Web (React)
 
## Task
Build the frontend and backend for a chat application.
Use Firebase Authentication for auth and Realtime Database for real-time message sync.

Implementing Firebase Authentication

Let's build the authentication layer that forms the foundation of our chat app. Simply tell Antigravity's agent to "create the auth component," and it generates code like the following.

Creating the Auth Hook

// src/hooks/useAuth.ts
import { useState, useEffect } from "react";
import {
  getAuth,
  onAuthStateChanged,
  signInWithEmailAndPassword,
  createUserWithEmailAndPassword,
  signInWithPopup,
  GoogleAuthProvider,
  signOut,
  type User,
} from "firebase/auth";
import { getDatabase, ref, set, serverTimestamp } from "firebase/database";
 
export function useAuth() {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState(true);
  const auth = getAuth();
  const db = getDatabase();
 
  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (firebaseUser) => {
      setUser(firebaseUser);
      setLoading(false);
 
      // Save user profile to database
      if (firebaseUser) {
        set(ref(db, `users/${firebaseUser.uid}`), {
          displayName: firebaseUser.displayName || "Anonymous",
          email: firebaseUser.email,
          photoURL: firebaseUser.photoURL,
          lastSeen: serverTimestamp(),
        });
      }
    });
    return unsubscribe;
  }, [auth, db]);
 
  const login = (email: string, password: string) =>
    signInWithEmailAndPassword(auth, email, password);
 
  const register = (email: string, password: string) =>
    createUserWithEmailAndPassword(auth, email, password);
 
  const loginWithGoogle = () => {
    const provider = new GoogleAuthProvider();
    return signInWithPopup(auth, provider);
  };
 
  const logout = () => signOut(auth);
 
  return { user, loading, login, register, loginWithGoogle, logout };
}
// Expected output: useAuth() manages auth state and provides login/register/logout functions

Real-Time Message Sync with Realtime Database

Now let's implement the core logic for sending and receiving messages in real time using Firebase Realtime Database.

Message Hook

// src/hooks/useChat.ts
import { useState, useEffect, useCallback } from "react";
import {
  getDatabase,
  ref,
  push,
  onChildAdded,
  query,
  orderByChild,
  limitToLast,
  serverTimestamp,
  off,
} from "firebase/database";
import type { User } from "firebase/auth";
 
interface Message {
  id: string;
  text: string;
  senderId: string;
  senderName: string;
  senderPhoto: string | null;
  timestamp: number;
}
 
export function useChat(roomId: string, user: User | null) {
  const [messages, setMessages] = useState<Message[]>([]);
  const db = getDatabase();
 
  useEffect(() => {
    if (!roomId) return;
 
    const messagesRef = query(
      ref(db, `rooms/${roomId}/messages`),
      orderByChild("timestamp"),
      limitToLast(100)
    );
 
    // Listen for new messages in real time
    const handleNewMessage = onChildAdded(messagesRef, (snapshot) => {
      const data = snapshot.val();
      const message: Message = {
        id: snapshot.key!,
        text: data.text,
        senderId: data.senderId,
        senderName: data.senderName,
        senderPhoto: data.senderPhoto,
        timestamp: data.timestamp,
      };
      setMessages((prev) => [...prev, message]);
    });
 
    return () => {
      off(messagesRef, "child_added", handleNewMessage);
    };
  }, [roomId, db]);
 
  const sendMessage = useCallback(
    async (text: string) => {
      if (!user || !text.trim()) return;
 
      const messagesRef = ref(db, `rooms/${roomId}/messages`);
      await push(messagesRef, {
        text: text.trim(),
        senderId: user.uid,
        senderName: user.displayName || "Anonymous",
        senderPhoto: user.photoURL,
        timestamp: serverTimestamp(),
      });
    },
    [user, roomId, db]
  );
 
  return { messages, sendMessage };
}
// Expected output: Messages sync in real time and a send function is available

Chat UI Component

// src/components/ChatRoom.tsx
import { useState, useRef, useEffect } from "react";
import { useAuth } from "../hooks/useAuth";
import { useChat } from "../hooks/useChat";
 
export function ChatRoom({ roomId }: { roomId: string }) {
  const { user } = useAuth();
  const { messages, sendMessage } = useChat(roomId, user);
  const [input, setInput] = useState("");
  const bottomRef = useRef<HTMLDivElement>(null);
 
  // Auto-scroll when new messages arrive
  useEffect(() => {
    bottomRef.current?.scrollIntoView({ behavior: "smooth" });
  }, [messages]);
 
  const handleSend = async () => {
    if (!input.trim()) return;
    await sendMessage(input);
    setInput("");
  };
 
  return (
    <div className="chat-room">
      <div className="messages">
        {messages.map((msg) => (
          <div
            key={msg.id}
            className={`message ${msg.senderId === user?.uid ? "own" : "other"}`}
          >
            <img
              src={msg.senderPhoto || "/default-avatar.png"}
              alt={msg.senderName}
              className="avatar"
            />
            <div className="bubble">
              <span className="sender">{msg.senderName}</span>
              <p>{msg.text}</p>
              <time>{new Date(msg.timestamp).toLocaleTimeString("en-US")}</time>
            </div>
          </div>
        ))}
        <div ref={bottomRef} />
      </div>
      <div className="input-area">
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyDown={(e) => e.key === "Enter" && handleSend()}
          placeholder="Type a message..."
        />
        <button onClick={handleSend}>Send</button>
      </div>
    </div>
  );
}

Security Rules and Push Notifications

Firebase Realtime Database Security Rules

{
  "rules": {
    "rooms": {
      "$roomId": {
        "messages": {
          ".read": "auth != null",
          ".write": "auth != null",
          "$messageId": {
            ".validate": "newData.hasChildren(['text', 'senderId', 'senderName', 'timestamp'])
              && newData.child('senderId').val() === auth.uid
              && newData.child('text').val().length > 0
              && newData.child('text').val().length <= 1000"
          }
        }
      }
    },
    "users": {
      "$uid": {
        ".read": "auth != null",
        ".write": "auth.uid === $uid"
      }
    }
  }
}

These rules ensure only authenticated users can read and write messages, prevent impersonation, and limit messages to 1000 characters.

Push Notifications with Cloud Messaging

// src/services/notifications.ts
import { getMessaging, getToken, onMessage } from "firebase/messaging";
 
export async function setupNotifications() {
  const messaging = getMessaging();
 
  // Request notification permission
  const permission = await Notification.requestPermission();
  if (permission !== "granted") {
    console.log("Notifications were denied");
    return null;
  }
 
  // Get the FCM token
  const token = await getToken(messaging, {
    vapidKey: process.env.NEXT_PUBLIC_FIREBASE_VAPID_KEY,
  });
 
  // Handle foreground messages
  onMessage(messaging, (payload) => {
    const { title, body } = payload.notification || {};
    if (title) {
      new Notification(title, { body, icon: "/icon-192.png" });
    }
  });
 
  return token;
}
// Expected output: FCM token retrieved and push notification reception enabled

Frequently Asked Questions

Q: How do I give Antigravity's agents my Firebase configuration?

A: Create an agents.md file at your project root with your Firebase project ID, database URL, and authentication methods. The agent references this file when generating code, ensuring project-specific context is accurately reflected. For more details, see "agents.md Guide".

Q: Should I use Realtime Database or Firestore?

A: For use cases where real-time responsiveness is critical, like chat apps, Realtime Database offers superior latency. If you need complex queries or large-scale data, Firestore is the better choice. This guide uses Realtime Database to prioritize low latency.

Q: How many users can I support on the free tier?

A: Firebase's Spark (free) plan supports up to 100 simultaneous connections and 1GB of data storage for Realtime Database. This is sufficient for small team chats or personal projects. As your user base grows, we recommend upgrading to the Blaze (pay-as-you-go) plan.

Q: Can the app handle offline message sending?

A: Yes. Firebase Realtime Database has built-in offline caching. By enabling enablePersistence(), messages sent while offline are queued locally and automatically synced when the connection is restored.

A Note from an Indie Developer

Wrapping Up: Maximize Productivity with Agent-Driven Development

In this guide, we covered how to build a real-time chat app using Antigravity's agents and Firebase:

  • Firebase Authentication: Email/password and Google sign-in
  • Realtime Database: Real-time message sync with onChildAdded
  • Security Rules: Impersonation prevention and input validation
  • Cloud Messaging: Push notification implementation

By providing the right context through agents.md, you can implement these complex features efficiently. For Firebase basics, check out "Firebase × Antigravity Integration Guide". For advanced agent configuration, see "Multi-Agent Orchestration Guide".

Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

If you found this article helpful, a small tip ($1.50) would mean a lot to us. Your support helps keep this site ad-free and covers server and hosting costs.

Related Articles

Agents & Manager2026-07-16
When to Hand Your Agent the Next Instruction: Waiting, Interrupting, and Queuing, Measured
Antigravity v2.3.0 added message queuing and Send Now. I measured waiting, interrupting, and queuing against the same yardstick, found that 41% of queued instructions arrive stale, and cut rework from 22% to 9% with a twenty-line stamp.
Agents & Manager2026-07-13
Passing API Keys to Agents Safely: Runtime Env Injection and Log Redaction
How to hand secrets to an Antigravity agent without leaving them in your repo or your logs, using runtime environment injection and output masking.
Agents & Manager2026-07-10
Tracing Why an Agent Wrote That Line Six Months Ago — Commit Granularity and Provenance Trailers
When an agent packs 14 files and 800 lines into a single commit, git blame tells you nothing six months later. Here is how I split commits at intent boundaries, recorded provenance as machine-readable Git trailers, and built a one-command path from a blamed line back to the design decision behind it.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →