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.

Real-time Features
This application leverages Geobase’s real-time capabilities to provide:
- Broadcast: Send ephemeral cursor position updates between clients with low latency
 - Presence: Track and synchronize active users and their states
 - 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:
- Cursor Broadcasting:
 
const channel = Geobase.channel('room_01')
  .on('broadcast', { event: 'mouse-move' }, ({ payload }) => {
    updateCursorPosition(payload)
  })- User Presence:
 
const presenceChannel = Geobase.channel('presence_01')
  .on('presence', { event: 'sync' }, () => {
    const state = presenceChannel.presenceState()
    updatePresence(state)
  })- 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:
- Throttled Updates: Mouse movements are throttled to prevent channel overflow
 
const broadcastPosition = _.throttle((position) => {
  channel.broadcast('mouse-move', position)
}, 50)- 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:
- Real-Time Cursor Tracking: See other users’ cursor positions on the map in real-time
 - User Presence: Track active users with unique avatars and names
 - Pin Dropping: Users can drop permanent pins on the map
 - Spatial Database: Utilizes PostGIS for efficient geospatial queries
 

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_KEYYou can locate the project reference and anon key in your Geobase project settings.

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 devAccess 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 Overview
For additional information, refer to:
For support, join our Discord community.