USE CASE

Property boundaries that line up, with the data attached.

Magnetic snap to neighbouring parcels. Typed owner, zoning, and area fields. Clean polygons in, clean GeoJSON out.

The problem

Property-boundary data has two requirements most map editors don't serve together: topological cleanliness (adjacent parcels share exact vertices, not almost-shared ones) and structured ownership data (owner name, parcel ID, zoning class, recorded area). Solving one often means losing the other.

How Element Editor fits

Snapping is on by default — drawing against an existing parcel edge pulls the cursor to that exact vertex, so shared borders match to the last decimal. Typed schemas carry the data per parcel. Both ship in standard GeoJSON, ready for the cadastral system, the GIS import, or the public-records export.

Neighbouring parcels snap to each other's vertices. No gaps, no overlaps.

What matters most for this use case

  • Snapping — the single feature that solves the topological-cleanliness problem.
  • Schema-backed properties — parcel_id, owner, zoning, area as typed fields.
  • Boolean operations — merge two parcels, subdivide one with slice.
  • Vertex editing — precise adjustments; arc-length-based midpoint spacing handles dense border data (country edges, coastlines).

A minimal setup

import { ElementEditor } from '@element-editor/react'
import type { Schema } from '@element-editor/react'
import '@element-editor/react/style.css'

const parcelSchema: Schema = {
  id: 'parcel',
  name: 'Parcel',
  fields: [
    { key: 'parcel_id', label: 'Parcel ID', type: 'text', required: true },
    { key: 'owner', label: 'Owner', type: 'text', required: true },
    { key: 'zoning', label: 'Zoning', type: 'select', required: true,
      options: ['residential', 'commercial', 'industrial', 'agricultural', 'mixed'] },
    { key: 'area_sqm', label: 'Area (m²)', type: 'number', required: true, min: 0 },
    { key: 'recorded_at', label: 'Recorded', type: 'date', required: false },
    { key: 'notes', label: 'Notes', type: 'textarea', required: false },
  ],
}

export default function Parcels() {
  return (
    <div style={{ height: 600 }}>
      <ElementEditor
        schema={parcelSchema}
        tools={['polygon', 'rectangle']}
        onChange={(data) => syncToCadastralSystem(data)}
      />
    </div>
  )
}

Snapping is on by default, so a new parcel lands exactly on its neighbour's vertices and edges. Hold ⌘ / Ctrl while clicking when a corner should sit near a boundary but not on it.

Ready to try it?