FEATURE · DATA
GeoJSON in from anywhere. Out wherever you need it.
GeoJSON via paste, file picker, file drop, the OpenStreetMap picker, or your own code. Replace or merge. Download, copy, or read it programmatically on the way out.

Try it
- 1Click the market, press ⌘C then ⌘V to paste a copy
- 2Press I, then Copy or Download
- 3Press ⌘O to load a .geojson file
Moving data in and out is where most editors leak trust
A paste goes wrong and overwrites the user's ten-minute edit. A file import silently drops features with unsupported geometry types. An export produces technically-valid GeoJSON that no backend will accept. A merge import creates duplicate IDs.
Element Editor's import and export paths are explicit about these cases. Import reports skipped, duplicate, and truncated features, and re-importing data with the same ids is safe. Invalid initialData reports an error and starts the editor empty instead of crashing it. Export produces RFC 7946 with no proprietary wrappers — schemas and groups travel as metadata.schemas and metadata.groups foreign members on the FeatureCollection.
Import sources
Paste — ⌘V on the map accepts a Feature or FeatureCollection; pasted features always land as copies with new IDs. Or paste a whole FeatureCollection into the GeoJSON viewer.
File picker — ⌘O opens a file picker, replaces the editor's data with the chosen .geojson file (one undo step) and fits the map to it.
File drop — drop a .geojson / .json file anywhere on the drop-in editor and it imports like ⌘O. On by default; fileDrop={false} turns it off, and it's off in readonly. For your own drop zone (or with the adapters), use the headless useGeoJSONDrop hook (see below). Same parser, same validation.
Google Sheets (helpers) — @element-editor/core exports extractSheetId, fetchSheetCsv, parseCsv and csvToGeoJson for turning a publicly shared sheet into a FeatureCollection. There's no built-in button; feed the result to ref.importGeoJSON().
OpenStreetMap picker — press X, click anywhere on the map, and Element imports the nearest OSM feature as an editable shape. Named parks, buildings, roads, lakes, land use. See drawing for the details.
Programmatic — ref.importGeoJSON(data, mode) from your code. Useful for loading from a backend or hydrating with server-rendered data.
Initial data — the initialData prop. Element loads it once: on mount, or when it arrives later while the editor is still empty and unedited. The initial load doesn't fire onChange.
Import modes: replace or merge
importGeoJSON(data, mode) accepts 'replace' or 'merge':
- Replace — all existing features are removed and the imported collection takes over. It's one undo step, but no backup is written: call
ref.exportGeoJSON()first if you need a copy that survives a reload. - Merge — imported features are added alongside existing ones. Incoming features whose id is already in the editor are skipped and counted as
duplicates.
What import returns
ref.importGeoJSON() returns a result object:
{
skipped: number, // features without a usable geometry (malformed or null)
duplicates: number, // skipped: id repeated within the import, or (merge) already in the editor
truncated: boolean // true if the import hit maxFeatures or the internal 50,000-feature cap
}Use this to show a user-facing summary after an import. The data table and viewer both update automatically; no refresh needed.
Export destinations
Download — ref.exportAs('geojson') or the GeoJSON viewer's download button writes a .geojson file to disk. The FeatureCollection is RFC 7946 with metadata.schemas and metadata.groups attached.
Copy to clipboard — the viewer's Copy button puts the serialized FeatureCollection on the system clipboard.
Programmatic — ref.getFeatureCollection() returns the current state synchronously. Use it to POST to a backend, persist to IndexedDB, or hand off to your own pipeline.
For drop-in headless integrations there are two hooks in @element-editor/ui:
useGeoJSONDrop— returns spread-friendlygetRootProps/getInputPropsand anopen()trigger for a programmatic file picker. Wires drag-and-drop to a parsedFeatureCollectionyou route into Element however you like (ref API,initialData, custom merge).useGeoJSONExport— symmetric.exportToFile()triggers a download,exportToString()returns the serialized JSON,exportToBlob()returns aBlobfor upload /navigator.share.
Show it in code
import { useRef } from 'react'
import { ElementEditor, type ElementEditorRef, type FeatureCollection } from '@element-editor/react'
export default function App() {
const ref = useRef<ElementEditorRef>(null)
const loadFromBackend = async () => {
const data: FeatureCollection = await fetch('/api/features').then(r => r.json())
const result = ref.current?.importGeoJSON(data, 'replace')
console.log('imported', result) // { skipped, duplicates, truncated }
}
const saveToBackend = () => {
const data = ref.current?.getFeatureCollection()
fetch('/api/features', { method: 'POST', body: JSON.stringify(data) })
}
const download = () => {
ref.current?.exportAs('geojson')
}
return <ElementEditor ref={ref} />
}importGeoJSON and exportGeoJSON are plain functions on the ref. Nothing goes through React state for the data round-trip — the state only updates after the import commits.
Things to know before you ship
- Replace doesn't back up.
importGeoJSON(data, 'replace')andsetFeatureCollection(data)discard the current features. One undo brings them back, but nothing survives a reload: keepref.exportGeoJSON()first if you need that. - Import and export run on the main thread. Parsing a very large file can block the UI briefly; the drop hook rejects files over 100 MB by default.
maxFeaturescaps imports too. An import is truncated to the room left undermaxFeatures(existing features count in merge mode) and returnstruncated: true. Without it, the internal 50,000-feature limit applies.
Works with
- GeoJSON viewer — see exactly what import and export produce.
- Schemas — schemas travel with the data, both directions.
- Data table — verify imports quickly by glancing at the table.