This document shows exactly how to update your existing dashboard component to use Realtime updates.
Hook Implementation: The useRealtimeSubscription hook is located at client/src/hooks/useRealtimeSubscription.ts and accepts a table name (string) and query key (array) as parameters, with an optional third parameter for configuration options like { enabled: boolean }.
// File: client/src/pages/dashboard/landlord.tsx
import { useState, useEffect } from "react";
import { useAuth } from "@/hooks/useAuth";
import { useToast } from "@/hooks/use-toast";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@/lib/queryClient";
export default function LandlordDashboard() {
const { toast } = useToast();
const { user } = useAuth();
const queryClient = useQueryClient();
const [activeSection, setActiveSection] = useState("overview");
// Fetch properties
const { data: properties } = useQuery({
queryKey: ["properties"],
queryFn: () => apiRequest("/api/properties"),
});
// Fetch tenants
const { data: tenants } = useQuery({
queryKey: ["tenants"],
queryFn: () => apiRequest("/api/tenants"),
});
// Fetch payments
const { data: payments } = useQuery({
queryKey: ["payments"],
queryFn: () => apiRequest("/api/payments"),
});
return (
<div>
{/* Dashboard UI */}
</div>
);
}// File: client/src/pages/dashboard/landlord.tsx
import { useState, useEffect } from "react";
import { useAuth } from "@/hooks/useAuth";
import { useToast } from "@/hooks/use-toast";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { apiRequest } from "@/lib/queryClient";
import { useRealtimeSubscription } from "@/hooks/useRealtimeSubscription"; // ← ADD THIS
export default function LandlordDashboard() {
const { toast } = useToast();
const { user } = useAuth();
const queryClient = useQueryClient();
const [activeSection, setActiveSection] = useState("overview");
// Fetch properties
const { data: properties } = useQuery({
queryKey: ["properties"],
queryFn: () => apiRequest("/api/properties"),
});
// Fetch tenants
const { data: tenants } = useQuery({
queryKey: ["tenants"],
queryFn: () => apiRequest("/api/tenants"),
});
// Fetch payments
const { data: payments } = useQuery({
queryKey: ["payments"],
queryFn: () => apiRequest("/api/payments"),
});
// ✨ ADD THESE THREE LINES - That's it!
useRealtimeSubscription("properties", ["properties"]);
useRealtimeSubscription("tenants", ["tenants"]);
useRealtimeSubscription("payments", ["payments"]);
return (
<div>
{/* Dashboard UI - NO CHANGES NEEDED */}
</div>
);
}1 import + one subscription call per data table:
- Import the hook at the top (
useRealtimeSubscriptionfrom@/hooks/useRealtimeSubscription) - Add one subscription call for each table you want to monitor:
useRealtimeSubscription("properties", ["properties"])useRealtimeSubscription("tenants", ["tenants"])useRealtimeSubscription("payments", ["payments"])
That's it! Your dashboard now updates in real-time. 🎉
Note: The hook signature is
useRealtimeSubscription(tableName, queryKey, options?)where:
tableName(string): The database table to subscribe toqueryKey(string[]): The TanStack Query key to invalidate when changes occuroptions(optional): Configuration object, e.g.,{ enabled: boolean }for conditional subscriptions
Add this to your imports section:
import { useRealtimeSubscription } from "@/hooks/useRealtimeSubscription";Add these lines right after your useQuery hooks (before the return statement):
useRealtimeSubscription("properties", ["properties"]);
useRealtimeSubscription("tenants", ["tenants"]);
useRealtimeSubscription("payments", ["payments"]);
useRealtimeSubscription("leases", ["leases"]);
useRealtimeSubscription("units", ["units"]);- Save the file
- Open your dashboard
- Open browser console (F12)
- Look for messages like:
[Realtime] Setting up subscription for table: properties [Realtime] ✅ Successfully subscribed to properties changes
- Open your dashboard in two browser windows side by side
- In Window 1: Add a new property or tenant
- In Window 2: Watch it appear automatically! ✨
// client/src/pages/dashboard/landlord.tsx
useRealtimeSubscription("properties", ["properties"]);
useRealtimeSubscription("tenants", ["tenants"]);
useRealtimeSubscription("leases", ["leases"]);
useRealtimeSubscription("payments", ["payments"]);
useRealtimeSubscription("units", ["units"]);Properties Page:
// client/src/pages/dashboard/landlord-properties.tsx
useRealtimeSubscription("properties", ["properties"]);Tenants Page:
// client/src/pages/dashboard/landlord-tenants.tsx
useRealtimeSubscription("tenants", ["tenants"]);Leases Page:
// client/src/pages/dashboard/landlord-leases.tsx
useRealtimeSubscription("leases", ["leases"]);Payments Page:
// client/src/pages/dashboard/landlord-payments.tsx
useRealtimeSubscription("payments", ["payments"]);If you have a detail page that only loads when an ID is present:
function PropertyDetails({ propertyId }) {
const { data: property } = useQuery({
queryKey: ["properties", propertyId],
queryFn: () => apiRequest(`/api/properties?id=${propertyId}`),
enabled: !!propertyId,
});
// Only subscribe when propertyId exists
useRealtimeSubscription("properties", ["properties", propertyId], {
enabled: !!propertyId
});
return <div>{/* Property details */}</div>;
}If your stats depend on multiple tables:
function DashboardStats() {
const { data: stats } = useQuery({
queryKey: ["dashboard", "stats"],
queryFn: () => apiRequest("/api/dashboard/stats"),
});
// Subscribe to all tables that affect stats
useRealtimeSubscription("properties", ["properties"]);
useRealtimeSubscription("units", ["units"]);
useRealtimeSubscription("tenants", ["tenants"]);
useRealtimeSubscription("leases", ["leases"]);
useRealtimeSubscription("payments", ["payments"]);
return <StatsCards stats={stats} />;
}Show users when data is refreshing:
function PropertiesList() {
const { data: properties, isFetching } = useQuery({
queryKey: ["properties"],
queryFn: () => apiRequest("/api/properties"),
});
useRealtimeSubscription("properties", ["properties"]);
return (
<div className="relative">
{/* Subtle loading indicator */}
{isFetching && (
<div className="absolute top-4 right-4 z-10">
<div className="flex items-center gap-2 text-sm text-muted-foreground">
<div className="h-2 w-2 rounded-full bg-primary animate-pulse" />
Updating...
</div>
</div>
)}
{/* Your properties list */}
<PropertyList properties={properties || []} />
</div>
);
}Make sure you have these environment variables set:
Local (.env):
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your_anon_keyVercel (Project Settings → Environment Variables):
VITE_SUPABASE_URLVITE_SUPABASE_ANON_KEY
Supabase (Database → Replication):
- Enable replication for:
properties,tenants,leases,payments,units
Before:
- Users must manually refresh the page to see updates
- Changes made by other users are invisible
- Data can become stale
After:
- Updates appear instantly (within ~100ms)
- All users see changes in real-time
- Data is always fresh
- Better collaboration for multi-user scenarios
- More professional and modern UX
That's it! Three lines of code for real-time magic. ✨