TL;DR
Learn about the challenges and solutions for implementing collaborative features in your app. We built a real-time "Who's Online?" wall using Velt inside this tutorial.
Features include:
- Real-time display of online users and their cursors.
- User authentication and document context management.
- A customizable UI with commenting and sign-out options.
This guide lays the foundation for creating engaging, collaborative tools. To enhance it further, consider adding features like reactions, notifications, and secure sign-in methods.
Letโs start ๐!
Modern web applications are increasingly collaborative. Think about how natural it feels to see those colorful cursors moving around in Figma, or those profile bubbles in Google Docs showing who's viewing the document. These presence features have become essential for any collaborative app.
Did you know that 97% of users are more likely to stay engaged with a product when they can see other users actively collaborating in real-time? The psychology behind this is fascinating - we're naturally drawn to spaces where we can see others working alongside us, even in digital environments.
Challenges of Building a Presence Feature from Scratch
Building presence features seems straightforward at first - just track who's online, right? But as many developers discover, it quickly becomes complex. Take a recent project I worked on: we started with a simple websocket connection to show active users, but things got messy when we needed to handle unstable connections and browser tabs.
Let's look at the backend first. You'll need a robust system to maintain websocket connections across multiple server instances. Here's a common pattern using Redis:
// Server-side presence tracking
const presence = new Map();
redis.subscribe('presence', (channel, message) => {
const { userId, status } = JSON.parse(message);
presence.set(userId, status);
broadcastPresence();
});
The frontend brings its own challenges. Ever noticed how Google Docs shows you as "away" when you switch tabs? Implementing accurate presence states means handling various browser events:
// Frontend presence detection
window.addEventListener('visibilitychange', () => {
if (document.hidden) {
updatePresence('away');
} else {
updatePresence('active');
}
});
One of the trickiest parts is distinguishing between a user who's truly offline versus temporarily disconnected. You might think a simple timeout would work:
// WARNING: Oversimplified approach
socket.on('disconnect', () => {
setTimeout(() => markUserOffline(userId), 30000);
});
But real-world connections are messier. Users might have poor connectivity, their laptop might go to sleep, or they might just close their laptop without properly disconnecting. A more robust solution needs heartbeat mechanisms and cleanup functions to handle these edge cases.
For companies operating globally, multi-region support adds another layer of complexity. Your presence system needs to sync user states across different geographic regions with minimal latency. This often involves setting up presence servers in each region and implementing complex state reconciliation:
// Multi-region presence sync
function syncPresenceAcrossRegions(userId, status) {
const regions = ['us-east', 'eu-west', 'ap-south'];
regions.forEach(region => {
if (region !== currentRegion) {
notifyRegion(region, userId, status);
}
});
}
The good news? You don't have to build all of this from scratch anymore. Modern solutions handle these edge cases while giving you the flexibility to customize the experience for your users. Whether you're building the next Figma or adding basic collaboration to your app, understanding these challenges helps you make better architectural decisions.
Why SDKs Are becoming popular
Developers are turning to SDKs to build presence features because it's simply more practical than building everything from scratch. Here's why:
1. Time savings - Instead of spending weeks handling edge cases like network disconnections or browser tabs, you can integrate presence in a few hours.
2. Proven solutions - Popular SDKs have already solved common problems like:
- Managing flaky internet connections
- Handling multiple browser tabs
- Cleaning up when users leave
- Syncing presence across servers
3. Cost effective - The time and resources needed to build and maintain a custom presence system often costs more than using an SDK.
Real-world examples where SDKs make sense:
- Document editors showing who's viewing or editing
- Chat applications displaying online/offline status
- Design tools showing where other users are working
- Meeting platforms indicating who's currently speaking
- Data analytics platforms showing real-time collaborators on dashboards
- Video editing software displaying who's working on which timeline segments
The choice between building custom or using an SDK comes down to your specific needs. If you need basic presence features that just work, an SDK is usually the way to go. Custom solutions make sense mainly for unique requirements or when you need complete control over the implementation.
One of the most popular SDKs for building presence features is Velt. At its core, Velt provides a full suite of collaboration features found in popular apps like Figma, Google Docs etc. They handle the complex infrastructure needed for live collaboration features. It manages the WebSocket connections, state synchronization, and presence tracking across users and sessions.
What makes it particularly useful for developers is that it abstracts away the typical headaches of building real-time features - things like handling connection drops, managing presence across multiple tabs, and cleaning up stale sessions. The SDK provides ready-to-use components for common presence patterns while still allowing low-level access to the presence data when needed.
Setting up Velt in your project and adding presence
Let's build a real-time "Who's Online?" wall that shows active users on your website. We'll use Next.js 15 with TypeScript and Velt's presence features.
Here's how it's going to look:
Project Setup
First, create a new Next.js project with TypeScript:
npx create-next-app@latest whos-online --typescript --tailwind --app
cd whos-online
Install the Velt SDK:
npm install @veltdev/react
npm install --save-dev @veltdev/types
Configuration
Head over to the Velt Console and get your Velt API key. This will be used to authenticate your requests to the Velt API.
Then store this API key in your .env
file:
NEXT_PUBLIC_VELT_API_KEY=your_api_key
Create a new provider component to initialize Velt:
'use client'
import { VeltProvider as BaseVeltProvider } from "@veltdev/react"
export function VeltProvider({ children }: { children: React.ReactNode }) {
return (
<BaseVeltProvider apiKey={process.env.NEXT_PUBLIC_VELT_API_KEY!}>
{children}
</BaseVeltProvider>
)
}
Next, In your root component, wrap your app in the VeltProvider
component:
import type { Metadata } from "next";
import { Geist, Geist_Mono } from "next/font/google";
import "./globals.css";
import { VeltProvider } from "./provider/VeltProvider";
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = { // Next.js metadata
title: "Who's Online?",
description: "A real-time presence feature built with Velt",
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<VeltProvider> // VeltProvider is a component that provides the Velt context to the app
<html lang="en">
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
{children}
</body>
</html>
</VeltProvider>
);
}
Authenticating Users
First, let's create a type for our user data:
export interface UserData { // UserData is an interface that defines the structure of the user data
userId: string;
name: string;
email: string;
photoUrl?: string;
color: string;
textColor: string;
}
Now, we need a way to let Velt know who the user is. There is a useVeltClient
hook that you can use to identify the user. It works in the following way:
import { useVeltClient } from '@veltdev/react';
const { client } = useVeltClient();
// Perform authentication
client.identify(user); // here user is the user data that you want to identify the user with
Next, we also need to set the document context. This is the document that the user will be interacting with.
const { client } = useVeltClient();
useEffect(() => {
if (client) {
client.setDocument('unique-document-id', {documentName: 'Document Name'});
}
}, [client]);
The client.setDocument method takes two arguments:
- The first argument is the
documentId
. This is a unique identifier for the document that you want to set the context for. - The second argument is an object that contains the document metadata. This is a key-value pair object that can be used to store any metadata about the document.
In our simple "Who's Online?" app, we will ask the user to enter their name and email and then identify the user with Velt.
'use client'
import { useState, useEffect } from 'react';
import { useVeltClient } from '@veltdev/react';
import { UserData } from '../types';
import { User } from '@veltdev/types';
const generateRandomColor = () => { // generateRandomColor is a function that generates a random color
const hue = Math.floor(Math.random() * 360);
const pastelSaturation = 70;
const pastelLightness = 80;
return `hsl(${hue}, ${pastelSaturation}%, ${pastelLightness}%)`;
};
const getContrastColor = (backgroundColor: string) => { // getContrastColor is a function that returns a contrasting color for the given background color
const hsl = backgroundColor.match(/\d+/g)?.map(Number);
if (!hsl) return '#000000';
const lightness = hsl[2];
return lightness > 70 ? '#000000' : '#ffffff';
};
export function UserAuth() { // UserAuth is a component that allows the user to authenticate
const { client } = useVeltClient();
const [isAuthenticated, setIsAuthenticated] = useState(() => { // state to check if the user is authenticated
return !!localStorage.getItem('userData');
});
useEffect(() => {
const initializeUser = async () => { // function to set the user data
const savedUser = localStorage.getItem('userData');
if (savedUser && client) {
const userData: UserData = JSON.parse(savedUser);
await client.identify(userData as User);
client.setDocument('whos-online-wall', {documentName: 'Who\'s Online?'});
}
};
if (client) {
initializeUser();
}
}, [client]);
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const name = formData.get('name') as string;
const email = formData.get('email') as string;
if (!name || !email || !client) return;
const backgroundColor = generateRandomColor();
const userData: UserData = {
userId: email,
name,
email,
color: backgroundColor,
textColor: getContrastColor(backgroundColor)
};
localStorage.setItem('userData', JSON.stringify(userData));
await client.identify(userData as User);
client.setDocument('whos-online-wall', {documentName: 'Who\'s Online?'});
setIsAuthenticated(true);
};
if (isAuthenticated) return null; // if the user is authenticated, return null
return ( // actual component
<div className="fixed inset-0 bg-black/60 backdrop-blur-sm flex items-center justify-center z-50">
<div className="bg-white dark:bg-gray-800 p-8 rounded-xl shadow-2xl max-w-md w-full mx-4 border border-gray-100 dark:border-gray-700">
<h2 className="text-3xl font-bold mb-8 text-gray-800 dark:text-white text-center">
Welcome! ๐
<span className="block text-lg font-normal mt-2 text-gray-600 dark:text-gray-300">
Please introduce yourself
</span>
</h2>
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label htmlFor="name" className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Name
</label>
<input
type="text"
name="name"
id="name"
required
className="mt-1 block w-full rounded-lg border border-gray-300 dark:border-gray-600
px-4 py-3 bg-white dark:bg-gray-700 text-gray-900 dark:text-white
focus:ring-2 focus:ring-blue-500 focus:border-transparent
transition-colors duration-200"
placeholder="Enter your name"
/>
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Email
</label>
<input
type="email"
name="email"
id="email"
required
className="mt-1 block w-full rounded-lg border border-gray-300 dark:border-gray-600
px-4 py-3 bg-white dark:bg-gray-700 text-gray-900 dark:text-white
focus:ring-2 focus:ring-blue-500 focus:border-transparent
transition-colors duration-200"
placeholder="Enter your email"
/>
</div>
<button
type="submit"
className="w-full bg-blue-600 text-white rounded-lg px-4 py-3
hover:bg-blue-700 transform hover:scale-[1.02]
transition-all duration-200 font-medium text-base
focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2
shadow-lg hover:shadow-xl"
>
Join Now
</button>
</form>
</div>
</div>
);
}
Here, we're expecting a name and email from the user and then we're storing this data in local storage. At the same time, we're identifying the user with Velt using the client.identify
method.
Then we're setting the document context using the client.setDocument
method. You can learn more about authentication here and setting the document context here.
Let's see how this works in the browser:
Adding the Online Wall
Now that we have the user authenticated and the document context set, we can add the online wall to our app.
To do this, we'll use the VeltPresence
component. This component will automatically handle the presence tracking for us.
'use client'
import { useVeltClient, VeltPresence } from '@veltdev/react'; // import the VeltPresence component
export function OnlineWall() { // OnlineWall is a component that shows the online wall
const { client } = useVeltClient();
if (!client) return null; // if the client is not initialized, return null
return (
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 p-4">
<VeltPresence />
</div>
);
}
Now after joining the wall, you should see the online wall with the users who have joined the wall.
I joined from three different windows and you can see the users who have joined the wall. Make sure to join via two different browser profiles or on incognito tab to prevent conflict between the users.
But what if I wanted to add a custom UI to the wall? What if I wanted to allow users to leave the wall?
Let's see how we can do that.
Customizing the Online Wall and allowing users to leave
We can customize the online wall UI by using the usePresenceUsers
hook. This hook returns the list of users who are currently online. Then we can use this list to render the users in our own custom UI.
To allow users to leave the wall, we can use the client.signOutUser
method.
Here's the logoutButton for the wall:
'use client'
import { useVeltClient } from '@veltdev/react';
export function LogoutButton() {
const { client } = useVeltClient();
const handleLogout = async () => {
if (client) {
await client.signOutUser(); // this will sign out the user from the current document
localStorage.removeItem('userData'); // this will remove the user data from local storage
window.location.reload(); // this will reload the page
}
};
const isAuthenticated = !!localStorage.getItem('userData');
if (!isAuthenticated) return null;
return (
<button
onClick={handleLogout}
className="fixed top-4 right-4 px-4 py-2 bg-gray-100 dark:bg-gray-700
text-gray-700 dark:text-gray-200 rounded-lg hover:bg-gray-200
dark:hover:bg-gray-600 transition-colors duration-200
flex items-center gap-2 shadow-sm"
>
<span>Logout</span>
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4" />
<polyline points="16 17 21 12 16 7" />
<line x1="21" y1="12" x2="9" y2="12" />
</svg>
</button>
);
}
Let's now update the OnlineWall
component to use the usePresenceUsers
hook and update the UI to show the users in a nice grid.
'use client'
import { useVeltClient, usePresenceUsers } from '@veltdev/react';
import { motion } from 'framer-motion';
export function OnlineWall() {
const { client } = useVeltClient();
const presenceUsers = usePresenceUsers();
if (!client) return null;
// Add loading state check
if (!presenceUsers) {
return (
<div className="flex items-center justify-center min-h-[200px]">
<div className="relative">
{/* Outer spinning ring with gradient */}
<div className="w-16 h-16 rounded-full absolute animate-spin
bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500"
></div>
{/* Inner white circle */}
<div className="w-16 h-16 rounded-full absolute bg-background"></div>
{/* Middle spinning ring with gradient */}
<div className="w-12 h-12 rounded-full absolute top-2 left-2 animate-spin
bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500"
></div>
{/* Inner white circle */}
<div className="w-12 h-12 rounded-full absolute top-2 left-2 bg-background"></div>
{/* Center dot with pulse effect */}
<div className="w-8 h-8 rounded-full absolute top-4 left-4
bg-gradient-to-r from-blue-500 to-purple-500 animate-pulse"
></div>
</div>
</div>
);
}
// Get current user data from localStorage
const currentUserData = localStorage.getItem('userData');
const currentUser = currentUserData ? JSON.parse(currentUserData) : null;
// Sort users to put current user first
const sortedUsers = presenceUsers?.sort((a, b) => {
if (a.userId === currentUser?.userId) return -1;
if (b.userId === currentUser?.userId) return 1;
return 0;
});
return ( // actual component
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 p-4">
{sortedUsers?.map((user) => {
const isCurrentUser = user.userId === currentUser?.userId;
return (
<motion.div
key={user.userId}
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
whileHover={{ scale: 1.05 }}
className="relative group"
>
{isCurrentUser && (
<div className="absolute -top-2 -right-2 bg-blue-500 text-white text-xs px-2 py-1 rounded-full z-10 shadow-lg">
You
</div>
)}
<div
className={`rounded-lg p-4 h-full shadow-lg transition-all duration-300 group-hover:shadow-xl
${isCurrentUser ? 'ring-2 ring-blue-500 ring-offset-2 ring-offset-background' : ''}`}
style={{
backgroundColor: user.color || '#f0f0f0',
color: user.textColor || '#000000'
}}
>
<div className="flex items-center space-x-3">
{user.photoUrl ? (
<img
src={user.photoUrl}
alt={user.name}
className="w-12 h-12 rounded-full border-2 border-white/30"
/>
) : (
<div
className="w-12 h-12 rounded-full flex items-center justify-center text-xl font-bold border-2 border-white/30"
style={{ backgroundColor: 'rgba(255, 255, 255, 0.2)' }}
>
{user.name?.charAt(0).toUpperCase()}
</div>
)}
<div className="flex-1 min-w-0">
<h3 className="font-semibold truncate">{user.name}</h3>
<p className="text-sm opacity-75 truncate">{user.email}</p>
</div>
</div>
<div className="mt-3 flex items-center">
<motion.div
className="w-2 h-2 rounded-full bg-green-400 mr-2"
animate={{
scale: [1, 1.2, 1],
}}
transition={{
duration: 2,
repeat: Infinity,
}}
/>
<span className="text-sm">Online now</span>
</div>
</div>
</motion.div>
);
})}
</div>
);
}
Here we show the user's in a nice grid and we also indicate the current user with a "You" badge.
Here I am also showing a loading state when the presence users are being fetched. This can be done using the usePresenceUsers
hook and checking if the users are null.
Here's how it looks:
Logging out of the wall is as simple as clicking the logout button.
How does it look?
How easy was that? With just a few lines of code, we've added a presence feature to our app.
Showing cursors in the online wall
Let's take a step further and show the cursors of the users in the online wall. This means that whatever the user is doing in the document, we'll show the cursor of the user in the online wall.
To do this, we'll use the VeltCursor
component. This component will automatically handle the cursor tracking for us. It's as simple as adding the component to the root.
What's particularly powerful about Velt's cursor implementation is that it doesn't just track raw x,y coordinates - it intelligently adapts to the underlying content structure. This means that even if users have different screen sizes, zoom levels, or different layouts (like responsive design changes), the cursors will always appear in the correct position relative to the content they're interacting with. This semantic understanding of the document structure ensures consistent cursor positioning across all clients.
Here the root is the OnlineWall
component. It can be handled better in a separate root component but let's keep it simple for now.
<>
<VeltCursor /> // just add this to the root
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 p-4">
{sortedUsers?.map((user) => {
const isCurrentUser = user.userId === currentUser?.userId;
return (
<motion.div
key={user.userId}
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
whileHover={{ scale: 1.05 }}
className="relative group"
>
{isCurrentUser && (
<div className="absolute -top-2 -right-2 bg-blue-500 text-white text-xs px-2 py-1 rounded-full z-10 shadow-lg">
You
</div>
)}
<div
className={`rounded-lg p-4 h-full shadow-lg transition-all duration-300 group-hover:shadow-xl
${isCurrentUser ? 'ring-2 ring-blue-500 ring-offset-2 ring-offset-background' : ''}`}
style={{
backgroundColor: user.color || '#f0f0f0',
color: user.textColor || '#000000'
}}
>
<div className="flex items-center space-x-3">
{user.photoUrl ? (
<img
src={user.photoUrl}
alt={user.name}
className="w-12 h-12 rounded-full border-2 border-white/30"
/>
) : (
<div
className="w-12 h-12 rounded-full flex items-center justify-center text-xl font-bold border-2 border-white/30"
style={{ backgroundColor: 'rgba(255, 255, 255, 0.2)' }}
>
{user.name?.charAt(0).toUpperCase()}
</div>
)}
<div className="flex-1 min-w-0">
<h3 className="font-semibold truncate">{user.name}</h3>
<p className="text-sm opacity-75 truncate">{user.email}</p>
</div>
</div>
<div className="mt-3 flex items-center">
<motion.div
className="w-2 h-2 rounded-full bg-green-400 mr-2"
animate={{
scale: [1, 1.2, 1],
}}
transition={{
duration: 2,
repeat: Infinity,
}}
/>
<span className="text-sm">Online now</span>
</div>
</div>
</motion.div>
);
})}
</div>
</>
We're wrapping the component in a fragment because we're adding the VeltCursor
component to the root and this allows to add more components to the root.
This can be maintained in a better way but this is a quick and dirty solution.
Adding comments to our online wall
What if we wanted to make the wall more interactive and allow users to leave comments anywhere on the wall? This becomes really simple with Velt.
We can use the VeltComments
and the VeltCommentTool
components to allow users to leave comments anywhere on the wall.
Here's how it works:
<>
<VeltCursor />
<VeltComments />
<div className="fixed bottom-4 right-4 z-50">
<VeltCommentTool />
</div>
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 p-4">
{sortedUsers?.map((user) => {
const isCurrentUser = user.userId === currentUser?.userId;
return (
<motion.div
key={user.userId}
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.9 }}
whileHover={{ scale: 1.05 }}
className="relative group"
>
{isCurrentUser && (
<div className="absolute -top-2 -right-2 bg-blue-500 text-white text-xs px-2 py-1 rounded-full z-10 shadow-lg">
You
</div>
)}
<div
className={`rounded-lg p-4 h-full shadow-lg transition-all duration-300 group-hover:shadow-xl
${isCurrentUser ? 'ring-2 ring-blue-500 ring-offset-2 ring-offset-background' : ''}`}
style={{
backgroundColor: user.color || '#f0f0f0',
color: user.textColor || '#000000'
}}
>
<div className="flex items-center space-x-3">
{user.photoUrl ? (
<img
src={user.photoUrl}
alt={user.name}
className="w-12 h-12 rounded-full border-2 border-white/30"
/>
) : (
<div
className="w-12 h-12 rounded-full flex items-center justify-center text-xl font-bold border-2 border-white/30"
style={{ backgroundColor: 'rgba(255, 255, 255, 0.2)' }}
>
{user.name?.charAt(0).toUpperCase()}
</div>
)}
<div className="flex-1 min-w-0">
<h3 className="font-semibold truncate">{user.name}</h3>
<p className="text-sm opacity-75 truncate">{user.email}</p>
</div>
</div>
<div className="mt-3 flex items-center">
<motion.div
className="w-2 h-2 rounded-full bg-green-400 mr-2"
animate={{
scale: [1, 1.2, 1],
}}
transition={{
duration: 2,
repeat: Infinity,
}}
/>
<span className="text-sm">Online now</span>
</div>
</div>
</motion.div>
);
})}
</div>
</>
Let's see how the final app looks like:
It's so simple! Now our wall is more interactive and engaging.
Summary of the Project
In this project, we've built a real-time "Who's Online?" wall using Velt. We've learned how to:
- Set up Velt in our project
- Authenticate users with Velt
- Set the document context for the current user
- Use the
VeltPresence
component to show the online users - Customize the online wall UI using the
usePresenceUsers
hook - Allow users to leave the wall and sign out
- Show cursors of the users in the online wall
- Allow users to leave comments anywhere on the wall
Benefits of using Velt
Building presence features from scratch often starts as a fun weekend project. You set up WebSocket connections, track user states, and get a basic version working locally. Then reality hits - handling disconnections, syncing across regions, and managing browser quirks turns that weekend project into weeks of debugging.
Velt handles these complexities while still giving you the control you need. Instead of wrestling with WebSocket reconnection logic or debouncing presence updates, you can focus on crafting the actual user experience. It's like using a battle-tested authentication system instead of rolling your own crypto - sure, you could build it, but why would you?
The real value shows up when your app grows. When that small team collaboration tool suddenly needs to handle hundreds of concurrent users, or when your US-only app needs to expand globally, you don't need to rewrite your presence infrastructure. The same SDK that handled 10 users will scale to handle thousands.
Some practical benefits developers appreciate:
- No need to maintain WebSocket servers and connection logic
- Built-in handling for network issues and browser tab synchronization
- Simple integration with existing authentication systems
- Automatic cleanup of stale presence data
Think of it like using Redis for caching - could you build your own caching system? Absolutely. But Redis gives you a proven solution that just works, letting you solve your actual business problems instead.
The best part is that you're not locked into a specific implementation. Want to show presence differently in different parts of your app? Need to add custom presence states? The SDK gives you the building blocks while letting you control how everything looks and behaves.
Additional Velt Features
Beyond presence and real-time cursors, Velt offers several powerful collaboration features:
Live Reactions - Add floating emojis and reactions that appear in real-time
Follow Mode - Let users follow each other's movements and actions. Perfect for presentations and guided tutorials
Huddles - Create instant audio/video meeting spaces within your app. Seamless integration with your existing UI
Live Selection - Select and highlight text in real-time. Perfect for collaborative editing
Video Player Sync - Sync video players across users. Perfect for video conferencing and presentations
Check more features here
Conclusion
In this article, we've seen how easy it is to add a presence feature to your app using Velt. We've learned how to set up Velt, authenticate users, set the document context, and use the VeltPresence
component to show the online users. We've also seen how to customize the online wall UI using the usePresenceUsers
hook and allow users to leave the wall and sign out.
You can further enhance our online wall by adding more features like real-time reactions, follow me mode, huddles, notifications and so much more!
In our wall, we did not implement a secure way to sign in users. We'll leave that as an exercise for you to implement. You can use any social login provider or email/password authentication and configure the Velt auth provider to use it.
I hope this article has given you a good understanding of how to build a presence feature into your app using Velt.
Happy coding!
To explore more about Velt and get started, check out:
Top comments (8)
Amazing Article ๐๐ผ
Thanks
Great Share
Thanks!
pretty detailed guide, helpful one
Thankyou
Interesting tutorial.
Thanks for sharing.
Glad you found interesting!