Build real-time features with WebSockets including chat, notifications, and live updates in Google Antigravity.
# WebSocket Real-time Chat Implementation for Google Antigravity
Real-time features are essential for modern applications. Google Antigravity's Gemini 3 helps you implement WebSocket connections for chat, notifications, live updates, and collaborative features with intelligent code suggestions.
## WebSocket Server Setup
Create a WebSocket server with proper connection management:
```typescript
// server/websocket.ts
import { WebSocketServer, WebSocket } from "ws";
import { IncomingMessage } from "http";
import { parse } from "url";
import { verifyToken } from "@/lib/auth";
interface AuthenticatedWebSocket extends WebSocket {
userId?: string;
isAlive: boolean;
}
interface Message {
type: string;
payload: unknown;
room?: string;
}
class WebSocketManager {
private wss: WebSocketServer;
private rooms: Map<string, Set<AuthenticatedWebSocket>> = new Map();
private userConnections: Map<string, Set<AuthenticatedWebSocket>> = new Map();
constructor(port: number) {
this.wss = new WebSocketServer({ port });
this.initialize();
}
private initialize() {
this.wss.on("connection", async (ws: AuthenticatedWebSocket, req: IncomingMessage) => {
const { query } = parse(req.url || "", true);
const token = query.token as string;
try {
const user = await verifyToken(token);
ws.userId = user.id;
ws.isAlive = true;
if (!this.userConnections.has(user.id)) {
this.userConnections.set(user.id, new Set());
}
this.userConnections.get(user.id)!.add(ws);
this.setupHandlers(ws);
this.sendToSocket(ws, { type: "connected", payload: { userId: user.id } });
} catch (error) {
ws.close(4001, "Authentication failed");
}
});
setInterval(() => {
this.wss.clients.forEach((ws: AuthenticatedWebSocket) => {
if (!ws.isAlive) {
return ws.terminate();
}
ws.isAlive = false;
ws.ping();
});
}, 30000);
}
private setupHandlers(ws: AuthenticatedWebSocket) {
ws.on("pong", () => {
ws.isAlive = true;
});
ws.on("message", (data: Buffer) => {
try {
const message: Message = JSON.parse(data.toString());
this.handleMessage(ws, message);
} catch (error) {
this.sendToSocket(ws, { type: "error", payload: "Invalid message format" });
}
});
ws.on("close", () => {
this.handleDisconnect(ws);
});
}
private handleMessage(ws: AuthenticatedWebSocket, message: Message) {
switch (message.type) {
case "join_room":
this.joinRoom(ws, message.room!);
break;
case "leave_room":
this.leaveRoom(ws, message.room!);
break;
case "room_message":
this.broadcastToRoom(message.room!, message.payload, ws);
break;
case "direct_message":
this.sendToUser(message.payload as { userId: string; content: string });
break;
default:
console.log("Unknown message type:", message.type);
}
}
private joinRoom(ws: AuthenticatedWebSocket, roomId: string) {
if (!this.rooms.has(roomId)) {
this.rooms.set(roomId, new Set());
}
this.rooms.get(roomId)!.add(ws);
this.sendToSocket(ws, { type: "room_joined", payload: { roomId } });
}
private leaveRoom(ws: AuthenticatedWebSocket, roomId: string) {
this.rooms.get(roomId)?.delete(ws);
}
private broadcastToRoom(roomId: string, payload: unknown, sender?: AuthenticatedWebSocket) {
const room = this.rooms.get(roomId);
if (!room) return;
const message = JSON.stringify({ type: "room_message", payload, roomId });
room.forEach((client) => {
if (client !== sender && client.readyState === WebSocket.OPEN) {
client.send(message);
}
});
}
private sendToUser(data: { userId: string; content: string }) {
const connections = this.userConnections.get(data.userId);
if (!connections) return;
const message = JSON.stringify({ type: "direct_message", payload: data.content });
connections.forEach((ws) => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(message);
}
});
}
private sendToSocket(ws: WebSocket, data: object) {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(data));
}
}
private handleDisconnect(ws: AuthenticatedWebSocket) {
if (ws.userId) {
this.userConnections.get(ws.userId)?.delete(ws);
}
this.rooms.forEach((clients) => {
clients.delete(ws);
});
}
public broadcast(message: object) {
const data = JSON.stringify(message);
this.wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
}
}
export const wsManager = new WebSocketManager(3001);
```
## React WebSocket Hook
Create a reusable hook for WebSocket connections:
```typescript
// hooks/useWebSocket.ts
"use client";
import { useEffect, useRef, useState, useCallback } from "react";
interface WebSocketOptions {
url: string;
token: string;
onMessage?: (data: unknown) => void;
onConnect?: () => void;
onDisconnect?: () => void;
reconnectAttempts?: number;
reconnectInterval?: number;
}
export function useWebSocket(options: WebSocketOptions) {
const {
url,
token,
onMessage,
onConnect,
onDisconnect,
reconnectAttempts = 5,
reconnectInterval = 3000,
} = options;
const wsRef = useRef<WebSocket | null>(null);
const reconnectCountRef = useRef(0);
const [isConnected, setIsConnected] = useState(false);
const [error, setError] = useState<string | null>(null);
const connect = useCallback(() => {
const ws = new WebSocket(`${url}?token=${token}`);
ws.onopen = () => {
setIsConnected(true);
setError(null);
reconnectCountRef.current = 0;
onConnect?.();
};
ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
onMessage?.(data);
} catch (e) {
console.error("Failed to parse message:", e);
}
};
ws.onclose = () => {
setIsConnected(false);
onDisconnect?.();
if (reconnectCountRef.current < reconnectAttempts) {
reconnectCountRef.current++;
setTimeout(connect, reconnectInterval);
}
};
ws.onerror = () => {
setError("WebSocket connection error");
};
wsRef.current = ws;
}, [url, token, onMessage, onConnect, onDisconnect, reconnectAttempts, reconnectInterval]);
useEffect(() => {
connect();
return () => {
wsRef.current?.close();
};
}, [connect]);
const send = useCallback((data: object) => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify(data));
}
}, []);
const joinRoom = useCallback((roomId: string) => {
send({ type: "join_room", room: roomId });
}, [send]);
const leaveRoom = useCallback((roomId: string) => {
send({ type: "leave_room", room: roomId });
}, [send]);
return { isConnected, error, send, joinRoom, leaveRoom };
}
```
## Chat Component Example
Build a real-time chat interface:
```typescript
// components/Chat.tsx
"use client";
import { useState, useEffect, useRef } from "react";
import { useWebSocket } from "@/hooks/useWebSocket";
interface ChatMessage {
id: string;
userId: string;
content: string;
timestamp: Date;
}
export function Chat({ roomId, token }: { roomId: string; token: string }) {
const [messages, setMessages] = useState<ChatMessage[]>([]);
const [input, setInput] = useState("");
const messagesEndRef = useRef<HTMLDivElement>(null);
const { isConnected, send, joinRoom } = useWebSocket({
url: process.env.NEXT_PUBLIC_WS_URL!,
token,
onMessage: (data: { type: string; payload: ChatMessage }) => {
if (data.type === "room_message") {
setMessages((prev) => [...prev, data.payload]);
}
},
onConnect: () => joinRoom(roomId),
});
useEffect(() => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages]);
const handleSend = () => {
if (!input.trim()) return;
send({
type: "room_message",
room: roomId,
payload: { content: input, timestamp: new Date() },
});
setInput("");
};
return (
<div className="chat-container">
<div className="status">
{isConnected ? "Connected" : "Disconnected"}
</div>
<div className="messages">
{messages.map((msg) => (
<div key={msg.id} className="message">
<strong>{msg.userId}:</strong> {msg.content}
</div>
))}
<div ref={messagesEndRef} />
</div>
<div className="input-area">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === "Enter" && handleSend()}
placeholder="Type a message..."
/>
<button onClick={handleSend} disabled={!isConnected}>
Send
</button>
</div>
</div>
);
}
```
## Best Practices
1. **Implement heartbeat** - Detect and clean up stale connections
2. **Handle reconnection** - Automatically reconnect with exponential backoff
3. **Authenticate connections** - Verify tokens before accepting connections
4. **Use rooms for scaling** - Group related connections for efficient broadcasting
5. **Clean up on unmount** - Close connections when components unmount
WebSocket integration with Google Antigravity enables powerful real-time features with intelligent connection management patterns.This websocket prompt is ideal for developers working on:
By using this prompt, you can save hours of manual coding and ensure best practices are followed from the start. It's particularly valuable for teams looking to maintain consistency across their websocket implementations.
Yes! All prompts on Antigravity AI Directory are free to use for both personal and commercial projects. No attribution required, though it's always appreciated.
This prompt works excellently with Claude, ChatGPT, Cursor, GitHub Copilot, and other modern AI coding assistants. For best results, use models with large context windows.
You can modify the prompt by adding specific requirements, constraints, or preferences. For websocket projects, consider mentioning your framework version, coding style, and any specific libraries you're using.