Build a realtime presence web application using SurrealDB live queries
SurrealDB offers various features including a realtime notification mechanism called Live Queries. This feature allows you to subscribe to changes in your database and receive notifications in real-time. In this guide, you’ll learn how to implement realtime presence tracking that can be integrated in any application including chat applications, multiplayer games, and more. The demo project is available on GitHub using the following tech stack:
SurrealDB our database
TanStack Query to fetch and store data retrieved from the database
Tailwind for styling our application
Hello, are you there?

The demo application looks like a simple chat application with basic features allowing users to join a room and send messages. The application also displays the number of users in the room and their presence status. The presence status is updated in real-time using SurrealDB Live Queries. The mechanism used to detect the presence of a user is a periodic ping sent by the client to the server.
The following configuration is used to setup the project:
Signal user presence in room periodically every 10 seconds
Display status badge based on idle time
🟩 < 2 minutes of inactivity
🟨 < 10 minutes of inactivity
⬜ beyond 10 minutes of inactivity
Note: Those values are completely arbitrary and can be changed to fit your needs.
Architecture
This project is using the following folder structure:
/schemas- list of SurrealDB tables/events- list of SurrealDB events/migrations- list of db migrations that will be automatically applied/src/api- TanStack query hooks/components/constants/contexts- Theme and SurrealDB providers/hooks- custom React hooks/lib- functions and app models/mutations- surql query files to create or update data, using SurrealDB events/pages/queries- surql query files to query the database, using SurrealDB tables
Prerequisites
Before you begin this tutorial you’ll need the following:
a. SurrealDB installed on your machine (Make sure you upgrade to the latest version if you already have SurrealDB installed on your machine)
b. The Bun runtime
c. Optional: surrealdb-migrations to manage and automate the deployment of your SurrealDB schema
d. A basic understanding of React and TanStack Query
Step 0: Setup the project
Once everything is installed, clone the project and navigate to it. Then:
Start a new SurrealDB instance locally
Apply migrations to the database
Either apply schema & migrations automatically by running the following command:
Or manually apply each file stored in the following folders:
schemaseventsmigrationsInstall dependencies and run the web app
Launch your web browser on the generated url (eg. http://localhost:5173/) and play with the app: create new accounts, join rooms, leave rooms, etc..
Step 1: Authentication
For users to join rooms and interact with the app, we need users. And thankfully, SurrealDB also offers authentication mechanism. We will need some basic authentication such as a registration form, a login form, and a way to sign out.
The user table will look like this:
We can then create a login form and a sign up dialog.
Note
Step 2: Display room information
We first start by writing the query that will be used to display room information.
Each *.surql query file can then be linked to a TanStack Query query, like this one:
Here, we will expose a new hook that encapsulates a useQuery hook underneath. The same can be done with TanStack query mutations.
Step 3: Signal user presence
Signaling a presence from the client is almost too easy.
We use the usePageVisibility hook to ensure the user is still looking at our app, meaning he did not put the app in the background. Note: this hook is using the Page Visibility API underneath.
And if the page is visible, we use the useInterval hook to trigger the signal event every 10 seconds via a TanStack Query mutation .
The mutation will trigger the following SurrealDB event:
The presence table will store every presence detection event triggered by our application.
Step 4: Display realtime presence status
Storing all the presence detection triggered is interesting but it's not very useful. We want to display the presence status of each user in realtime efficiently. To do so, we will create a new table called last_presence to retrieve the last presence detection event for each user. We will then be able to use this table to display the presence status of each user in realtime..
Displaying the presence status badge of a user is quite simple. We just need to retrieve the last presence detection event for the user and display the presence status badge based on the time difference between the last presence detection event and the current time.
Note
Now, this component can be easily integrated into another components, like this one:
For reference, we query the last presence of the current user with this query:
The useRealtimeCurrentUserPresence hook retrieves the last presence of the current user and is composed of multiple hooks:
useCurrentUserPresence- the base hook to retrieve the current user presence (without realtime capability)useCurrentUserPresenceLive- the base hook that is notified by each changes in the database (pure realtime capability)useRealtimeCurrentUserPresence- the hook itself that combines both previous hooks (current value + upcoming changes)
Step 5: Refactoring with the useLiveQuery hook
One can notice the presence of the useLiveQuery hook. This hook is a custom hook that we created to simplify then lifecycle of a Live Query. It automatically subscribe to the Live Query when enabled and it will kill the Live Query on cleanup (when the component is unmounted). Correctly cleaning Live Queries would prevent from any memory leak.
Bonus: the simulator
Being alone is not really that fun, isn't it? We can't really test the realtime presence feature without having multiple users connected to the same room. That's why we can use the simulator script built to generate some fake users that will interact with the app while active. You can run the following command to start the simulator:
And then let's enjoy the nature of randomness make the app alive!
Resources
Live demo - The live demo of the application
GitHub repository - The GitHub repository of the application
SurrealDB Live Queries - The SurrealDB Live Queries documentation
SurrealQL Documentation - The SurrealQL documentation
Javascript SDK Documentation - The Javascript SDK documentation