USE CASE
A map editor that fits field-team workflows.
Typed forms for every feature. Standard GeoJSON out. Works with any tile source, including offline-cached tiles. Keyboard-first for fast data entry.
The problem
Field teams collect geographic data — inspection points, survey lines, boundary polygons, incident markers. Each item type needs its own fields. Teams often work with unreliable connectivity and on pre-cached basemaps. Generic map editors force every item through the same form and assume live tiles.
How Element Editor fits
Define one schema per item type. Field users draw with the matching tool (point for inspections, line for surveyed paths, area for boundaries) and pick the item type in the property panel, which then shows that schema's form. The data exports as standard GeoJSON — compatible with GIS systems, mobile-sync pipelines, and any backend that speaks RFC 7946.
Screenshot: a satellite-imagery map with several dropped point markers, a surveyed polyline, and an outlined polygon region. The active feature is a point with an 'Inspection' form showing inspector_id, condition (select), notes (textarea), photo_taken (boolean), inspected_at (date).
What matters most for this use case
- Multiple schemas per collection — points, lines, and polygons can all live in one FeatureCollection, each with its own schema (they travel as
metadata.schemas). - Custom basemaps — add satellite imagery, pre-cached offline tiles, or a private internal tile server next to the built-in styles.
- Keyboard shortcuts — every tool is one keystroke; field users spend their day in it.
- Import and export — round-trip with a field app's sync layer; schemas survive.
A minimal setup
import { ElementEditor } from '@element-editor/react'
import '@element-editor/react/style.css'
export default function FieldApp() {
return (
<div style={{ height: 600 }}>
<ElementEditor
initialData={existingSurveyData}
basemap={{
id: 'field-imagery',
label: 'Field imagery',
url: 'https://offline-tiles.my-org.com/{z}/{x}/{y}.png',
attribution: 'Internal imagery',
}}
basemaps={false}
onChange={(data) => syncPendingChanges(data)}
/>
</div>
)
}For offline-first setups, pair the onChange callback with a local IndexedDB store (or a service worker cache) so the sync to your backend happens when connectivity returns.