react-ol v1.3.0

Examples

Practical examples and common use cases

Examples

This page contains practical examples and common use cases for react-ol.

Basic Examples

Simple Map

The most basic map with a tile layer:

import { OpenLayersMap, MapView, MapTileLayer } from '@mixelburg/react-ol';
import { OSM } from 'ol/source';
import 'ol/ol.css';

function App() {
  return (
    <OpenLayersMap wrapperProps={{ style: { width: '100%', height: '600px' }}}>
      <MapView defaultCenter={{ lat: 32.0853, long: 34.7818 }} defaultZoom={10} />
      <MapTileLayer source={new OSM()} />
    </OpenLayersMap>
  );
}

Map with Markers

Add interactive markers to your map:

import { OpenLayersMap, MapTileLayer, MapVectorLayer, PointFeature } from '@mixelburg/react-ol';
import { OSM } from 'ol/source';
import { Circle, Fill, Stroke, Style } from 'ol/style';

const markerStyle = new Style({
  image: new Circle({
    radius: 8,
    fill: new Fill({ color: '#ef4444' }),
    stroke: new Stroke({ color: 'white', width: 2 })
  })
});

function App() {
  return (
    <OpenLayersMap wrapperProps={{ style: { width: '100%', height: '600px' }}}>
      <MapView defaultCenter={{ lat: 32.0853, long: 34.7818 }} defaultZoom={13} />
      <MapTileLayer source={new OSM()} />

      <MapVectorLayer layerId="markers">
        <PointFeature
          coordinates={{ lat: 32.0853, long: 34.7818 }}
          style={markerStyle}
        />
        <PointFeature
          coordinates={{ lat: 32.0900, long: 34.7900 }}
          style={markerStyle}
        />
      </MapVectorLayer>
    </OpenLayersMap>
  );
}

Interactive Examples

Click Handler

Handle map and feature clicks:

import { useState } from 'react';
import { OpenLayersMap, MapTileLayer, MapVectorLayer, PointFeature } from '@mixelburg/react-ol';

function App() {
  const [lastClick, setLastClick] = useState(null);
  const [selectedMarker, setSelectedMarker] = useState(null);

  return (
    <div>
      <div style={{ padding: 16 }}>
        {lastClick && <div>Last click: {lastClick.lat.toFixed(4)}, {lastClick.long.toFixed(4)}</div>}
        {selectedMarker && <div>Selected: {selectedMarker}</div>}
      </div>

      <OpenLayersMap
        defaultCenter={{ lat: 32.0853, long: 34.7818 }}
        defaultZoom={13}
        onClick={(coords) => setLastClick(coords)}
        wrapperProps={{ style: { width: '100%', height: '600px' }}}
      >
        <MapTileLayer source={new OSM()} />

        <MapVectorLayer layerId="markers">
          <PointFeature
            coordinates={{ lat: 32.0853, long: 34.7818 }}
            properties={{ name: 'Marker 1' }}
            onClick={(feature, event) => {
              event.stopPropagation(); // Prevent map click
              setSelectedMarker(feature.get('name'));
            }}
          />
        </MapVectorLayer>
      </OpenLayersMap>
    </div>
  );
}

Hover Effects

Show hover effects on features:

import { Circle, Fill, Stroke, Style } from 'ol/style';

const normalStyle = new Style({
  image: new Circle({
    radius: 8,
    fill: new Fill({ color: '#3b82f6' }),
    stroke: new Stroke({ color: 'white', width: 2 })
  })
});

const hoverStyle = new Style({
  image: new Circle({
    radius: 12,
    fill: new Fill({ color: '#ef4444' }),
    stroke: new Stroke({ color: 'white', width: 3 })
  })
});

function App() {
  return (
    <OpenLayersMap>
      <MapView defaultCenter={{ lat: 32, long: 34 }} defaultZoom={13} />
      <MapTileLayer source={new OSM()} />

      <MapVectorLayer layerId="markers">
        <PointFeature
          coordinates={{ lat: 32.0853, long: 34.7818 }}
          style={normalStyle}
          hoverStyle={hoverStyle}
          onMouseEnter={(feature) => console.log('Hover start')}
          onMouseExit={(feature) => console.log('Hover end')}
        />
      </MapVectorLayer>
    </OpenLayersMap>
  );
}

Controlled Map

Control map state externally:

import { useState } from 'react';
import { OpenLayersMap, MapTileLayer } from '@mixelburg/react-ol';

function App() {
  const [center, setCenter] = useState({ lat: 32.0853, long: 34.7818 });
  const [zoom, setZoom] = useState(10);

  const locations = [
    { name: 'Tel Aviv', coords: { lat: 32.0853, long: 34.7818 } },
    { name: 'Jerusalem', coords: { lat: 31.7683, long: 35.2137 } },
    { name: 'Haifa', coords: { lat: 32.7940, long: 34.9896 } },
  ];

  return (
    <div>
      <div style={{ padding: 16, display: 'flex', gap: 8 }}>
        {locations.map(loc => (
          <button
            key={loc.name}
            onClick={() => {
              setCenter(loc.coords);
              setZoom(13);
            }}
          >
            Go to {loc.name}
          </button>
        ))}
        <button onClick={() => setZoom(zoom + 1)}>Zoom In</button>
        <button onClick={() => setZoom(zoom - 1)}>Zoom Out</button>
      </div>

      <OpenLayersMap
        center={center}
        onCenterChange={setCenter}
        zoom={zoom}
        onZoomChange={setZoom}
        wrapperProps={{ style: { width: '100%', height: '600px' }}}
      >
        <MapTileLayer source={new OSM()} />
      </OpenLayersMap>
    </div>
  );
}

Advanced Examples

Feature Clustering

Handle large numbers of points efficiently with clustering:

import { OpenLayersMap, MapTileLayer, MapVectorLayer, PointFeature } from '@mixelburg/react-ol';
import { OSM } from 'ol/source';
import { Circle, Fill, Style, Text } from 'ol/style';

// Generate sample data points
const points = Array.from({ length: 200 }, (_, i) => ({
  id: i,
  coords: {
    lat: 32.0 + (Math.random() - 0.5) * 0.5,
    long: 34.7 + (Math.random() - 0.5) * 0.5,
  }
}));

function App() {
  return (
    <OpenLayersMap
      defaultCenter={{ lat: 32.0853, long: 34.7818 }}
      defaultZoom={11}
      wrapperProps={{ style: { width: '100%', height: '600px' }}}
    >
      <MapTileLayer source={new OSM()} />

      <MapVectorLayer
        layerId="clustered-points"
        clustering={{
          enabled: true,
          distance: 40  // Distance in pixels for clustering
        }}
        style={(feature) => {
          const features = feature.get('features');
          const size = features?.length || 1;

          if (size === 1) {
            // Style for individual point
            return new Style({
              image: new Circle({
                radius: 6,
                fill: new Fill({ color: '#3b82f6' })
              })
            });
          }

          // Style for cluster
          return new Style({
            image: new Circle({
              radius: 10 + Math.min(size / 5, 10),
              fill: new Fill({ color: '#ef4444' })
            }),
            text: new Text({
              text: size.toString(),
              fill: new Fill({ color: 'white' })
            })
          });
        }}
      >
        {points.map(point => (
          <PointFeature
            key={point.id}
            coordinates={point.coords}
          />
        ))}
      </MapVectorLayer>
    </OpenLayersMap>
  );
}

Drawing Routes

Draw lines between points:

import { useState } from 'react';
import { OpenLayersMap, MapTileLayer, MapVectorLayer, PointFeature, LineStringFeature } from '@mixelburg/react-ol';
import { Circle, Fill, Stroke, Style } from 'ol/style';

function App() {
  const [points, setPoints] = useState([]);

  const handleMapClick = (coords) => {
    setPoints([...points, coords]);
  };

  const clearPoints = () => setPoints([]);

  return (
    <div>
      <div style={{ padding: 16 }}>
        <button onClick={clearPoints}>Clear Route</button>
        <span style={{ marginLeft: 16 }}>Points: {points.length}</span>
      </div>

      <OpenLayersMap
        defaultCenter={{ lat: 32.0853, long: 34.7818 }}
        defaultZoom={13}
        onClick={handleMapClick}
        wrapperProps={{ style: { width: '100%', height: '600px' }}}
      >
        <MapTileLayer source={new OSM()} />

        <MapVectorLayer layerId="route">
          {/* Draw line through all points */}
          {points.length > 1 && (
            <LineStringFeature
              coordinates={points}
              style={new Style({
                stroke: new Stroke({
                  color: '#3b82f6',
                  width: 3
                })
              })}
            />
          )}

          {/* Draw markers at each point */}
          {points.map((point, index) => (
            <PointFeature
              key={index}
              coordinates={point}
              style={new Style({
                image: new Circle({
                  radius: 6,
                  fill: new Fill({ color: '#ef4444' }),
                  stroke: new Stroke({ color: 'white', width: 2 })
                })
              })}
            />
          ))}
        </MapVectorLayer>
      </OpenLayersMap>
    </div>
  );
}

Polygon Drawing

Draw and edit polygons:

import { useState } from 'react';
import { OpenLayersMap, MapTileLayer, MapVectorLayer, PolygonFeature, PointFeature } from '@mixelburg/react-ol';
import { Fill, Stroke, Style, Circle as CircleStyle } from 'ol/style';

function App() {
  const [vertices, setVertices] = useState([]);
  const [isComplete, setIsComplete] = useState(false);

  const handleMapClick = (coords) => {
    if (!isComplete) {
      setVertices([...vertices, coords]);
    }
  };

  const completePolygon = () => {
    if (vertices.length >= 3) {
      setIsComplete(true);
    }
  };

  const reset = () => {
    setVertices([]);
    setIsComplete(false);
  };

  return (
    <div>
      <div style={{ padding: 16 }}>
        <button onClick={completePolygon} disabled={vertices.length < 3 || isComplete}>
          Complete Polygon
        </button>
        <button onClick={reset}>Reset</button>
        <span style={{ marginLeft: 16 }}>
          Vertices: {vertices.length} {!isComplete && '(click map to add)'}
        </span>
      </div>

      <OpenLayersMap
        defaultCenter={{ lat: 32.0853, long: 34.7818 }}
        defaultZoom={13}
        onClick={handleMapClick}
        wrapperProps={{ style: { width: '100%', height: '600px' }}}
      >
        <MapTileLayer source={new OSM()} />

        <MapVectorLayer layerId="polygon">
          {/* Draw polygon when complete */}
          {isComplete && vertices.length >= 3 && (
            <PolygonFeature
              coordinates={vertices}
              style={new Style({
                fill: new Fill({ color: 'rgba(59, 130, 246, 0.2)' }),
                stroke: new Stroke({ color: '#3b82f6', width: 2 })
              })}
            />
          )}

          {/* Draw vertices */}
          {vertices.map((vertex, index) => (
            <PointFeature
              key={index}
              coordinates={vertex}
              style={new Style({
                image: new CircleStyle({
                  radius: 6,
                  fill: new Fill({ color: '#ef4444' }),
                  stroke: new Stroke({ color: 'white', width: 2 })
                })
              })}
            />
          ))}
        </MapVectorLayer>
      </OpenLayersMap>
    </div>
  );
}

Data Visualization

Display data points with different sizes/colors:

import { OpenLayersMap, MapTileLayer, MapVectorLayer, PointFeature } from '@mixelburg/react-ol';
import { Circle, Fill, Stroke, Style } from 'ol/style';

const cities = [
  { name: 'Tel Aviv', coords: { lat: 32.0853, long: 34.7818 }, population: 460000 },
  { name: 'Jerusalem', coords: { lat: 31.7683, long: 35.2137 }, population: 936000 },
  { name: 'Haifa', coords: { lat: 32.7940, long: 34.9896 }, population: 285000 },
  { name: 'Rishon LeZion', coords: { lat: 31.9730, long: 34.7925 }, population: 254000 },
];

function getMarkerStyle(population) {
  // Scale marker size based on population
  const radius = Math.sqrt(population) / 100;

  // Color based on population
  const color = population > 500000 ? '#ef4444' :
                population > 300000 ? '#f59e0b' : '#3b82f6';

  return new Style({
    image: new Circle({
      radius: radius,
      fill: new Fill({ color: color }),
      stroke: new Stroke({ color: 'white', width: 2 })
    })
  });
}

function App() {
  return (
    <OpenLayersMap
      defaultCenter={{ lat: 32, long: 35 }}
      defaultZoom={9}
      wrapperProps={{ style: { width: '100%', height: '600px' }}}
    >
      <MapTileLayer source={new OSM()} />

      <MapVectorLayer layerId="cities">
        {cities.map(city => (
          <PointFeature
            key={city.name}
            coordinates={city.coords}
            style={getMarkerStyle(city.population)}
            properties={city}
            onClick={(feature) => {
              alert(`${feature.get('name')}: ${feature.get('population').toLocaleString()} people`);
            }}
          />
        ))}
      </MapVectorLayer>
    </OpenLayersMap>
  );
}

Real-time Tracking

Animate a moving marker:

import { useState, useEffect } from 'react';
import { OpenLayersMap, MapTileLayer, MapVectorLayer, PointFeature, LineStringFeature, useMapRef } from '@mixelburg/react-ol';
import { Circle, Fill, Stroke, Style } from 'ol/style';

function App() {
  const [position, setPosition] = useState({ lat: 32.0853, long: 34.7818 });
  const [trail, setTrail] = useState([]);
  const [isTracking, setIsTracking] = useState(false);
  const mapRef = useMapRef();

  useEffect(() => {
    if (!isTracking) return;

    const interval = setInterval(() => {
      setPosition(prev => {
        const newPos = {
          lat: prev.lat + (Math.random() - 0.5) * 0.001,
          long: prev.long + (Math.random() - 0.5) * 0.001,
        };
        setTrail(t => [...t.slice(-50), newPos]); // Keep last 50 points

        // Auto-center on marker
        mapRef.current?.centerOn(newPos);

        return newPos;
      });
    }, 1000);

    return () => clearInterval(interval);
  }, [isTracking, mapRef]);

  return (
    <div>
      <div style={{ padding: 16 }}>
        <button onClick={() => setIsTracking(!isTracking)}>
          {isTracking ? 'Stop' : 'Start'} Tracking
        </button>
        <button onClick={() => setTrail([])}>Clear Trail</button>
      </div>

      <OpenLayersMap
        ref={mapRef}
        defaultCenter={position}
        defaultZoom={15}
        wrapperProps={{ style: { width: '100%', height: '600px' }}}
      >
        <MapTileLayer source={new OSM()} />

        <MapVectorLayer layerId="tracking">
          {/* Trail */}
          {trail.length > 1 && (
            <LineStringFeature
              coordinates={trail}
              style={new Style({
                stroke: new Stroke({
                  color: '#3b82f6',
                  width: 2,
                  lineDash: [5, 5]
                })
              })}
            />
          )}

          {/* Current position */}
          <PointFeature
            coordinates={position}
            style={new Style({
              image: new Circle({
                radius: 10,
                fill: new Fill({ color: '#ef4444' }),
                stroke: new Stroke({ color: 'white', width: 3 })
              })
            })}
          />
        </MapVectorLayer>
      </OpenLayersMap>
    </div>
  );
}

Custom Popups

Create custom popups with React components:

import { useState } from 'react';
import { OpenLayersMap, MapTileLayer, MapVectorLayer, PointFeature, ReactMapOverlay } from '@mixelburg/react-ol';
import { Circle, Fill, Stroke, Style } from 'ol/style';

const locations = [
  { id: 1, name: 'Restaurant', coords: { lat: 32.0853, long: 34.7818 }, type: 'food', rating: 4.5 },
  { id: 2, name: 'Museum', coords: { lat: 32.0900, long: 34.7900 }, type: 'culture', rating: 4.8 },
  { id: 3, name: 'Park', coords: { lat: 32.0800, long: 34.7850 }, type: 'nature', rating: 4.2 },
];

function App() {
  const [selected, setSelected] = useState(null);

  return (
    <OpenLayersMap
      defaultCenter={{ lat: 32.0853, long: 34.7818 }}
      defaultZoom={14}
      wrapperProps={{ style: { width: '100%', height: '600px' }}}
      onClick={() => setSelected(null)}
    >
      <MapTileLayer source={new OSM()} />

      <MapVectorLayer layerId="places">
        {locations.map(location => (
          <PointFeature
            key={location.id}
            coordinates={location.coords}
            style={new Style({
              image: new Circle({
                radius: 8,
                fill: new Fill({
                  color: selected?.id === location.id ? '#ef4444' : '#3b82f6'
                }),
                stroke: new Stroke({ color: 'white', width: 2 })
              })
            })}
            onClick={(feature, event) => {
              event.stopPropagation();
              setSelected(location);
            }}
          />
        ))}
      </MapVectorLayer>

      {/* Popup */}
      {selected && (
        <ReactMapOverlay
          coordinates={selected.coords}
          transform="translate(-50%, -120%)"
        >
          <div style={{
            background: 'white',
            padding: 16,
            borderRadius: 8,
            boxShadow: '0 4px 12px rgba(0,0,0,0.2)',
            minWidth: 200,
            position: 'relative'
          }}>
            <button
              onClick={() => setSelected(null)}
              style={{
                position: 'absolute',
                top: 8,
                right: 8,
                border: 'none',
                background: 'transparent',
                fontSize: 20,
                cursor: 'pointer'
              }}
            >
              ×
            </button>

            <h3 style={{ margin: '0 0 8px 0' }}>{selected.name}</h3>
            <div style={{ fontSize: 14, color: '#666', marginBottom: 4 }}>
              Type: {selected.type}
            </div>
            <div style={{ fontSize: 14, color: '#f59e0b' }}>
              {'⭐'.repeat(Math.floor(selected.rating))} {selected.rating}
            </div>

            <button style={{
              marginTop: 12,
              padding: '6px 12px',
              background: '#3b82f6',
              color: 'white',
              border: 'none',
              borderRadius: 4,
              cursor: 'pointer',
              width: '100%'
            }}>
              View Details
            </button>
          </div>
        </ReactMapOverlay>
      )}
    </OpenLayersMap>
  );
}

Integration Examples

With React Hook Form

Integrate with forms:

import { useForm } from 'react-hook-form';
import { OpenLayersMap, MapTileLayer, MapVectorLayer, PointFeature } from '@mixelburg/react-ol';

function LocationForm() {
  const { register, handleSubmit, setValue, watch } = useForm({
    defaultValues: {
      name: '',
      lat: 32.0853,
      long: 34.7818
    }
  });

  const coordinates = {
    lat: watch('lat'),
    long: watch('long')
  };

  const onSubmit = (data) => {
    console.log('Form data:', data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <div style={{ display: 'flex', gap: 16, marginBottom: 16 }}>
        <input {...register('name')} placeholder="Location name" />
        <input type="number" step="any" {...register('lat', { valueAsNumber: true })} />
        <input type="number" step="any" {...register('long', { valueAsNumber: true })} />
        <button type="submit">Save</button>
      </div>

      <OpenLayersMap
        defaultCenter={coordinates}
        defaultZoom={13}
        onClick={(coords) => {
          setValue('lat', coords.lat);
          setValue('long', coords.long);
        }}
        wrapperProps={{ style: { width: '100%', height: '400px' }}}
      >
        <MapTileLayer source={new OSM()} />
        <MapVectorLayer layerId="marker">
          <PointFeature coordinates={coordinates} />
        </MapVectorLayer>
      </OpenLayersMap>
    </form>
  );
}

With State Management

Use with Redux/Zustand:

import create from 'zustand';
import { OpenLayersMap, MapTileLayer, MapVectorLayer, PointFeature } from '@mixelburg/react-ol';

// Zustand store
const useMapStore = create((set) => ({
  markers: [],
  addMarker: (coords) => set((state) => ({
    markers: [...state.markers, { id: Date.now(), coords }]
  })),
  removeMarker: (id) => set((state) => ({
    markers: state.markers.filter(m => m.id !== id)
  })),
  clearMarkers: () => set({ markers: [] })
}));

function App() {
  const { markers, addMarker, removeMarker, clearMarkers } = useMapStore();

  return (
    <div>
      <div style={{ padding: 16 }}>
        <button onClick={clearMarkers}>Clear All ({markers.length})</button>
      </div>

      <OpenLayersMap
        defaultCenter={{ lat: 32, long: 34 }}
        defaultZoom={10}
        onClick={(coords) => addMarker(coords)}
        wrapperProps={{ style: { width: '100%', height: '600px' }}}
      >
        <MapTileLayer source={new OSM()} />
        <MapVectorLayer layerId="markers">
          {markers.map(marker => (
            <PointFeature
              key={marker.id}
              coordinates={marker.coords}
              onClick={(feature, event) => {
                event.stopPropagation();
                removeMarker(marker.id);
              }}
            />
          ))}
        </MapVectorLayer>
      </OpenLayersMap>
    </div>
  );
}

See Also

On this page