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.

Local Development

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);

Database Setup

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

Access the application at http://localhost:3000

Local Development

Core Components

The application tracks user presence in real-time:

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

User Presence System

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
  });
};

Avatar Generation

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

  • public.pins: Stores map pin locations

Database Table

Database Overview

For additional information, refer to:

ℹ️

For support, join our Discord community.


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