Skip to Content
GuidesSoftware DeveloperReal-time (Next.js)

Real-time Collaborative Map Application

A real-time interactive map application built with Next.js, MapLibre GL, and Geobase that enables multiple users to collaborate on shared maps in real-time.

Collaborative Map Screenshot

Real-time Features

This application leverages Geobase’s real-time capabilities to provide:

  1. Broadcast: Send ephemeral cursor position updates between clients with low latency
  2. Presence: Track and synchronize active users and their states
  3. Database Changes: Listen to Postgres changes for collaborative features
ℹ️

Real-time features are powered by Geobase’s globally distributed cluster of Realtime servers.

MouseTracker Component

The MouseTracker component is the core of our real-time collaborative features. It enables:

  • Real-time cursor tracking for all connected users
  • Pin placement on map click
  • User presence tracking with avatars
  • Automatic cleanup of stale cursors

Technical Implementation

type CursorPosition = { userId: string; x: number; y: number; timestamp: number; }; type PresenceState = { user: string; online_at: string; avatar: string; };

Real-time Communication

The component utilizes three Geobase channels for different purposes:

  1. Cursor Broadcasting:
const channel = Geobase.channel('room_01') .on('broadcast', { event: 'mouse-move' }, ({ payload }) => { updateCursorPosition(payload) })
  1. User Presence:
const presenceChannel = Geobase.channel('presence_01') .on('presence', { event: 'sync' }, () => { const state = presenceChannel.presenceState() updatePresence(state) })
  1. Pin Updates:
const pinChannel = Geobase.channel('public:pins') .on('postgres_changes', { event: 'INSERT', schema: 'public' }, payload => { addNewPin(payload.new) } )
⚠️

Ensure proper cleanup of channel subscriptions when components unmount to prevent memory leaks.

Performance Optimizations

The component implements several optimizations:

  1. Throttled Updates: Mouse movements are throttled to prevent channel overflow
const broadcastPosition = _.throttle((position) => { channel.broadcast('mouse-move', position) }, 50)
  1. Automatic Cleanup: Stale cursors are removed after inactivity
const cleanupInterval = setInterval(() => { const now = Date.now() const staleTimeout = 5000 // 5 seconds Object.entries(cursors).forEach(([userId, cursor]) => { if (now - cursor.timestamp > staleTimeout) { removeCursor(userId) } }) }, 1000)
💡

The cleanup interval ensures the application remains performant even with many simultaneous users.

Map Integration

The component integrates with MapLibre GL through custom markers:

const addCursorToMap = (position: CursorPosition) => { const el = document.createElement('div') el.className = 'cursor-marker' new maplibregl.Marker(el) .setLngLat([position.x, position.y]) .addTo(map) }

User Presence System

User presence is managed through a combination of real-time channels and local state:

const initializePresence = async () => { const userId = generateUserId() const userState = { user: userId, online_at: new Date().toISOString(), avatar: getUserAvatar(userId) } await presenceChannel.track(userState) }
⚠️

Ensure proper cleanup of presence tracking when users leave the application.

What’s Included

This blueprint provides a complete real-time mapping solution with:

  1. Real-Time Cursor Tracking: See other users’ cursor positions on the map in real-time
  2. User Presence: Track active users with unique avatars and names
  3. Pin Dropping: Users can drop permanent pins on the map
  4. Spatial Database: Utilizes PostGIS for efficient geospatial queries

Real-time Collaborative Map

ℹ️

Ensure you have a Geobase project with PostGIS enabled before proceeding.

Development

Prerequisites

To get started, you’ll need:

  • Node.js (v14 or higher)
  • npm or yarn
  • Geobase account with PostGIS enabled

Environment Variables

To connect your app to Geobase, configure these environment variables. Include them in a .env.local file for local development.

NEXT_PUBLIC_GEOBASE_URL=https://YOUR_PROJECT_REF.geobase.app NEXT_PUBLIC_GEOBASE_ANON_KEY=YOUR_GEOBASE_PROJECT_ANON_KEY

You can locate the project reference and anon key in your Geobase project settings.

Shareable Maps

Local Development

  • Set Node.js version: Use Node version 21:
    nvm use 21
  • Install dependencies: Use your preferred package manager:
    npm install # or yarn # or pnpm install
  • Start the development server with HTTPS enabled:
    npm run dev -- --experimental-https # or yarn dev --experimental-https # or pnpm dev --experimental-https
ℹ️

Note: Without --experimental-https, the email verification links might redirect to https://localhost:3000/..., causing errors. You can navigate manually by removing https from the URL if needed.

Access the project at https://localhost:3000.

Database Setup

Run this SQL in your Geobase SQL editor:

create table public.pins ( id serial, user_id uuid not null, x double precision not null, y double precision not null, created_at timestamp without time zone null default now(), geom geometry(Point, 4326), constraint pins_pkey primary key (id) ); -- Add spatial indexing CREATE INDEX pins_geom_idx ON public.pins USING GIST (geom);

Local Development

# Install dependencies npm install # Start development server npm run dev
ℹ️

Access the application at http://localhost:3000

Core Components

User Presence System

The application tracks user presence in real-time:

type PresenceState = { user: string; online_at: string; };

Avatar Generation

Avatars are generated deterministically based on user IDs:

const getUserAvatar = (userId: string): string => { return generateAvatar({ style: AVATAR_STYLES[styleIndex], backgroundColor: BACKGROUND_COLORS[colorIndex], seed: userId }); };

Database Overview

The database structure supports real-time collaboration with these key tables:

  • public.pins: Stores map pin locations

Database Table

Learn More

For additional information, refer to:

ℹ️

For support, join our Discord community .


Source: GitHub - decision-labs/geobase-blueprint-realtime 

Last updated on