@softwarity/geojson-editor
v1.0.29
Published
A feature-rich GeoJSON editor Web Component with syntax highlighting, collapsible nodes, and color picker
Maintainers
Readme
@softwarity/geojson-editor
A feature-rich, framework-agnostic Web Component for editing GeoJSON features with syntax highlighting, collapsible nodes, and integrated color picker.
🚀 Try the Live Demo | 📋 Release Notes
Why not Monaco, CodeMirror, or Prism?
| | @softwarity/geojson-editor | Monaco Editor | CodeMirror 6 | Prism.js | |---|:---:|:---:|:---:|:---:| | Size (gzip) | | ~2.5 MB* | ~150 KB* | ~20 KB* | | Editable | ✅ Full editor | ✅ Full editor | ✅ Full editor | ❌ Read-only | | GeoJSON validation | ✅ Built-in | ❌ Manual | ❌ Manual | ❌ None | | Type highlighting | ✅ Contextual | ⚠️ Generic JSON | ⚠️ Generic JSON | ⚠️ Generic JSON | | Invalid type detection | ✅ Visual feedback | ❌ | ❌ | ❌ | | Collapsible nodes | ✅ Native | ✅ | ✅ Plugin | ❌ | | Undo/Redo | ✅ With grouping | ✅ | ✅ | ❌ | | Color picker | ✅ Integrated | ❌ | ❌ | ❌ | | Boolean checkbox | ✅ Integrated | ❌ | ❌ | ❌ | | Feature visibility toggle | ✅ | ❌ | ❌ | ❌ | | Auto-collapse coordinates | ✅ | ❌ | ❌ | ❌ | | FeatureCollection output | ✅ Always | ❌ | ❌ | ❌ | | Clear button | ✅ | ❌ | ❌ | ❌ | | Save to file (Ctrl+S) | ✅ | ❌ | ❌ | ❌ | | Open from file (Ctrl+O) | ✅ | ❌ | ❌ | ❌ | | Error navigation | ✅ | ✅ | ✅ | ❌ | | Dark mode detection | ✅ Auto | ⚠️ Manual | ⚠️ Manual | ⚠️ Manual | | Dependencies | 0 | Many | Few | 0 | | Setup complexity | 1 line | Complex | Moderate | Simple |
* Estimated total size: Monaco includes web workers loaded dynamically; CodeMirror/Prism require plugins for equivalent functionality (line numbers, folding, language support).
TL;DR: If you're building a GeoJSON-focused application and need a lightweight, specialized editor with built-in validation and GeoJSON-aware features, this component does exactly that — without the overhead of a general-purpose code editor.
Features
- Full Editor - Complete editing capabilities with cursor navigation, selection, copy/paste, and keyboard shortcuts — unlike read-only syntax highlighters
- GeoJSON-Aware Highlighting - Distinct colors for GeoJSON keywords (
type,coordinates,geometry, etc.) - GeoJSON Type Validation - Valid types (
Point,LineString,Polygon, etc.) highlighted distinctly; invalid types (LinearRing, unknown types) shown with error styling (colors configurable via theme) - Syntax Highlighting - JSON syntax highlighting with customizable color schemes
- Collapsible Nodes - Collapse/expand JSON objects and arrays with visual indicators (
{...}/[...]); use Enter to expand and Shift+Enter to collapse;coordinatesauto-collapsed on load - Attribute Navigation - Tab/Shift+Tab to navigate between JSON attributes (keys and values) for quick editing
- Virtualized Rendering - Monaco-like architecture: only visible lines are rendered to DOM for optimal performance with large GeoJSON files
- Feature Visibility Toggle - Hide/show individual Features via eye icon in gutter; hidden features are grayed out and excluded from
changeevents (useful for temporary filtering without deleting data) - Color Picker - Built-in color swatch for hex color properties (
#rrggbb) displayed inline next to the value; click to open native color picker - Boolean Checkbox - Inline checkbox for boolean properties displayed next to the value; toggle to switch between
true/falseand emit changes (e.g.,marker: trueto show vertices) - Dark/Light Color Schemes - Automatic color scheme based on system preference via CSS
light-dark()function - Auto-format - Automatic JSON formatting in real-time (always enabled)
- Readonly Mode - Visual indicator with diagonal stripes when editing is disabled
- Block Editing in Collapsed Areas - Prevents accidental edits in collapsed sections
- Smart Copy/Paste - Copy includes expanded content even from collapsed nodes
- FeatureCollection Output - Emits valid FeatureCollection with all edited features
- Clear Button - Discreet ✕ button in suffix area to clear all editor content (hidden in readonly mode)
- Undo/Redo - Full undo/redo support with Ctrl+Z / Ctrl+Y / Ctrl+Shift+Z; rapid keystrokes grouped as single undo step
- Save to File - Ctrl+S to download GeoJSON as
.geojsonfile; programmaticsave(filename)method available - Open from File - Ctrl+O to open a
.geojsonor.jsonfile from the client filesystem; programmaticopen()method available - Error Navigation - Visual error indicators in gutter with navigation buttons (◀ ▶) to jump between errors; error count displayed in suffix area
- Current Features Event -
current-featuresevent emitted as a FeatureCollection when cursor/selection changes; includes all features overlapping with selection; useful for synchronizing with maps
Installation
Option 1: CDN (No build step required)
Simply add a script tag to your HTML file:
<!-- Using unpkg -->
<script type="module" src="https://unpkg.com/@softwarity/geojson-editor"></script>
<!-- Or using jsDelivr -->
<script type="module" src="https://cdn.jsdelivr.net/npm/@softwarity/geojson-editor"></script>You can also specify a version:
<!-- Specific version -->
<script type="module" src="https://unpkg.com/@softwarity/[email protected]"></script>
<!-- Latest minor/patch of v1 -->
<script type="module" src="https://unpkg.com/@softwarity/geojson-editor@1"></script>Option 2: NPM (With bundler)
If you're using a bundler (Vite, Webpack, Rollup, etc.):
npm install @softwarity/geojson-editorThen import in your JavaScript/TypeScript:
import '@softwarity/geojson-editor';TypeScript Support
This package includes TypeScript type definitions out of the box. No additional @types/* package required.
import '@softwarity/geojson-editor';
import type { SetOptions, CursorPosition, GeometryType } from '@softwarity/geojson-editor';
// Type-safe editor access
const editor = document.querySelector('geojson-editor') as GeoJsonEditor;
// Full autocompletion and type checking
editor.set(features, { collapsed: ['coordinates'] });Exported types:
GeoJsonEditor- The Web Component classSetOptions- Options forset(),add(),insertAt(),open()CursorPosition- Cursor position{ line, column }GeometryType- GeoJSON geometry types union
Usage
Basic Usage
<!DOCTYPE html>
<html lang="en">
<head>
<script type="module" src="https://unpkg.com/@softwarity/geojson-editor"></script>
</head>
<body>
<!-- User edits features, component wraps in FeatureCollection -->
<geojson-editor placeholder="Enter GeoJSON features here..."></geojson-editor>
</body>
</html>Users edit features directly (comma-separated), and the component automatically wraps them in a {"type": "FeatureCollection", "features": [...]} structure for validation and events.
With Theme Control
The component automatically adapts to system color scheme. Override with CSS:
<!-- Force dark mode -->
<geojson-editor style="color-scheme: dark;"></geojson-editor>
<!-- Force light mode -->
<geojson-editor style="color-scheme: light;"></geojson-editor>Listen to Changes
const editor = document.querySelector('geojson-editor');
// Valid GeoJSON emits change event with parsed object directly
editor.addEventListener('change', (e) => {
console.log('GeoJSON:', e.detail); // Parsed GeoJSON object
});
// Invalid JSON or GeoJSON validation error emits error event
editor.addEventListener('error', (e) => {
console.error('Error:', e.detail.error);
console.log('Errors:', e.detail.errors); // Array of validation errors (if GeoJSON validation)
});Attributes
| Attribute | Type | Default | Description |
|-----------|------|---------|-------------|
| value | string | "" | Initial editor content (features array content) |
| placeholder | string | "" | Placeholder text |
| readonly | boolean | false | Make editor read-only |
Note: coordinates nodes are automatically collapsed when content is loaded to improve readability. Use Enter to expand and Shift+Enter to collapse nodes, or click the gutter toggle. Use Tab/Shift+Tab to navigate between attributes.
Themes
The component uses CSS light-dark() function for automatic dark/light mode switching based on the system's color-scheme preference.
Pre-built Themes
Ready-to-use theme CSS files are available:
| Theme | Description | CDN Link | |-------|-------------|----------| | Default (IntelliJ) | Built-in, no extra CSS needed | - | | VS Code | Visual Studio Code inspired colors | vscode.css | | GitHub | GitHub's code styling | github.css | | Monokai | Classic dark Monokai palette | monokai.css | | Solarized | Ethan Schoonover's precision colors | solarized.css |
Using via CDN:
<!-- Include theme CSS before the component -->
<link rel="stylesheet" href="https://unpkg.com/@softwarity/geojson-editor/themes/monokai.css">
<!-- Then use the component -->
<script type="module" src="https://unpkg.com/@softwarity/geojson-editor"></script>Using via npm:
// Import theme CSS in your build
import '@softwarity/geojson-editor/themes/github.css';
import '@softwarity/geojson-editor';Custom Themes
Create your own theme by overriding CSS custom properties on :root with the light-dark() function:
:root {
/* Editor background and text */
--geojson-editor-bg-color: light-dark(#ffffff, #1e1e1e);
--geojson-editor-text-color: light-dark(#000000, #d4d4d4);
--geojson-editor-caret-color: light-dark(#000000, #aeafad);
/* Gutter (line numbers area) */
--geojson-editor-gutter-bg: light-dark(#f3f3f3, #1e1e1e);
--geojson-editor-gutter-border: light-dark(#e0e0e0, #333333);
--geojson-editor-gutter-text: light-dark(#237893, #858585);
/* JSON syntax highlighting */
--geojson-editor-json-key: light-dark(#0451a5, #9cdcfe);
--geojson-editor-json-string: light-dark(#a31515, #ce9178);
--geojson-editor-json-number: light-dark(#098658, #b5cea8);
--geojson-editor-json-boolean: light-dark(#0000ff, #569cd6);
--geojson-editor-json-punct: light-dark(#000000, #d4d4d4);
--geojson-editor-json-error: light-dark(#cd3131, #f44747);
/* GeoJSON-specific */
--geojson-editor-geojson-key: light-dark(#800000, #9cdcfe);
--geojson-editor-geojson-type: light-dark(#008000, #4ec9b0);
--geojson-editor-geojson-type-invalid: light-dark(#cd3131, #f44747);
/* Controls (checkboxes, color swatches) */
--geojson-editor-control-color: light-dark(#0000ff, #569cd6);
--geojson-editor-control-bg: light-dark(#f3f3f3, #3c3c3c);
--geojson-editor-control-border: light-dark(#c0c0c0, #6b6b6b);
/* Selection and errors */
--geojson-editor-selection-color: light-dark(rgba(173, 214, 255, 0.5), rgba(38, 79, 120, 0.5));
--geojson-editor-error-color: light-dark(#cd3131, #f44747);
}Forcing Light or Dark Mode
/* Force dark mode on specific editor */
geojson-editor.dark-editor {
color-scheme: dark;
}
/* Force light mode */
geojson-editor.light-editor {
color-scheme: light;
}Integration with CSS Frameworks
The editor inherits color-scheme from parent elements. To integrate with frameworks that use class-based dark mode:
Tailwind CSS (uses html.dark):
html.dark { color-scheme: dark; }
html:not(.dark) { color-scheme: light; }Bootstrap 5 (uses [data-bs-theme]):
[data-bs-theme="dark"] { color-scheme: dark; }
[data-bs-theme="light"] { color-scheme: light; }DaisyUI / Other (any class-based theme):
.dark-theme { color-scheme: dark; }
.light-theme { color-scheme: light; }CSS Custom Properties Reference
| Variable | Description |
|----------|-------------|
| --geojson-editor-bg-color | Editor background |
| --geojson-editor-text-color | Default text color |
| --geojson-editor-caret-color | Cursor color |
| --geojson-editor-gutter-bg | Line numbers background |
| --geojson-editor-gutter-border | Gutter border color |
| --geojson-editor-gutter-text | Line numbers color |
| --geojson-editor-json-key | JSON property keys |
| --geojson-editor-json-string | String values |
| --geojson-editor-json-number | Number values |
| --geojson-editor-json-boolean | Boolean/null values |
| --geojson-editor-json-punct | Punctuation (brackets, colons) |
| --geojson-editor-json-error | Invalid JSON highlighting |
| --geojson-editor-geojson-key | GeoJSON keywords (type, geometry, etc.) |
| --geojson-editor-geojson-type | Valid geometry types (Point, Polygon, etc.) |
| --geojson-editor-geojson-type-invalid | Invalid geometry types |
| --geojson-editor-control-color | Inline controls accent color |
| --geojson-editor-control-bg | Inline controls background |
| --geojson-editor-control-border | Inline controls border |
| --geojson-editor-selection-color | Text selection background |
| --geojson-editor-error-color | Error indicators |
API Methods
const editor = document.querySelector('geojson-editor');Features API
Programmatic manipulation of features:
| Method | Description |
|--------|-------------|
| set(input, options?) | Replace all features (throws if invalid) |
| add(input, options?) | Add features at the end (throws if invalid) |
| insertAt(input, index, options?) | Insert at index (negative = from end: -1 = before last) (throws if invalid) |
| removeAt(index) | Remove feature at index (negative = from end), returns removed feature |
| removeAll() | Remove all features, returns array of removed features |
| get(index) | Get feature at index (negative = from end) |
| getAll() | Get all features as an array |
| emit() | Emit the current document on the change event |
Flexible Input: set(), add(), and insertAt() accept multiple input formats:
- FeatureCollection → extracts the
featuresarray - Feature[] → uses the array directly
- Feature → wraps in an array
Options Parameter: set(), add(), insertAt(), and open() accept an optional options object:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| collapsed | string[] | function | ['coordinates'] | Attributes to collapse after loading |
The collapsed option controls which JSON nodes are automatically collapsed:
string[]: Static list of attribute names to collapse (e.g.,['coordinates', 'properties'])function(feature, index) => string[]: Dynamic function returning attributes per feature'$root': Special keyword to collapse entire features- Empty array
[]: Disables auto-collapse (nothing collapsed)
// Default: only coordinates collapsed
editor.set(features);
// Collapse coordinates and properties
editor.set(features, { collapsed: ['coordinates', 'properties'] });
// Collapse entire features
editor.set(features, { collapsed: ['$root'] });
// No auto-collapse
editor.set(features, { collapsed: [] });
// Dynamic: collapse geometry only for Points
editor.set(features, {
collapsed: (feature, index) => {
if (feature.geometry?.type === 'Point') {
return ['geometry'];
}
return ['coordinates'];
}
});Validation: All input features are validated before adding. Invalid features throw an Error with a descriptive message. A valid Feature must have:
type: "Feature"geometry: object with valid type (Point,LineString,Polygon, etc.) andcoordinates, ornullproperties: object ornull
Smart Paste: When pasting GeoJSON content (Ctrl+V), the editor automatically detects and normalizes the format (FeatureCollection, Feature[], or single Feature). Invalid GeoJSON falls back to raw text insertion.
// Set features from array
editor.set([
{ type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] }, properties: {} },
{ type: 'Feature', geometry: { type: 'Point', coordinates: [1, 1] }, properties: {} }
]);
// Set from FeatureCollection
editor.set({
type: 'FeatureCollection',
features: [{ type: 'Feature', geometry: { type: 'Point', coordinates: [0, 0] }, properties: {} }]
});
// Set a single feature
editor.set({ type: 'Feature', geometry: { type: 'Point', coordinates: [2, 2] }, properties: {} });
// Add multiple features at once
editor.add([feature1, feature2]);
// Insert FeatureCollection at position 1
editor.insertAt(featureCollection, 1);
// Remove last feature
const removed = editor.removeAt(-1);
// Get all features
const features = editor.getAll();
// Manually emit change event
editor.emit();Undo/Redo API
Full undo/redo support with action grouping:
| Method | Description |
|--------|-------------|
| undo() | Undo last action, returns true if successful |
| redo() | Redo previously undone action, returns true if successful |
| canUndo() | Check if undo is available |
| canRedo() | Check if redo is available |
| clearHistory() | Clear undo/redo history |
Keyboard shortcuts:
Ctrl+Z/Cmd+Z- UndoCtrl+Y/Cmd+Y- RedoCtrl+Shift+Z/Cmd+Shift+Z- Redo (alternative)
Action grouping: Rapid keystrokes (< 500ms apart, same action type) are automatically grouped as a single undo step. This means typing "hello" quickly creates one undo entry, not five.
// Programmatic undo/redo
if (editor.canUndo()) {
editor.undo();
}
// Clear history after saving
editor.clearHistory();Save API
Save the current GeoJSON content to a file:
| Method | Description |
|--------|-------------|
| save(filename?) | Download GeoJSON as file, returns true if successful |
Keyboard shortcut:
Ctrl+S/Cmd+S- Save with default filename (features.geojson)
// Save with default filename
editor.save(); // Downloads "features.geojson"
// Save with custom filename
editor.save('my-map-data.geojson');Open API
Open a GeoJSON file from the client filesystem:
| Method | Description |
|--------|-------------|
| open(options?) | Open file dialog, returns Promise<boolean> (true if loaded successfully) |
Keyboard shortcut:
Ctrl+O/Cmd+O- Open file dialog
Supported formats:
- FeatureCollection (extracts features array)
- Single Feature (wraps in array)
- Array of Features
Note: The Ctrl+O shortcut is disabled in readonly mode, but open() remains available via API for programmatic loading.
// Open file dialog
const success = await editor.open();
if (success) {
console.log('File loaded:', editor.getAll());
}
// Open with custom collapse options
const success = await editor.open({ collapsed: ['$root'] });Error Navigation API
Navigate between syntax and structural errors:
| Method | Description |
|--------|-------------|
| goToNextError() | Navigate to next error, returns true if found |
| goToPrevError() | Navigate to previous error, returns true if found |
Error lines are indicated with a red bar in the gutter. The error count is displayed in the suffix area between navigation buttons (◀ ▶).
// Navigate to errors programmatically
editor.goToNextError();
editor.goToPrevError();Keyboard Shortcuts
| Shortcut | Action |
|----------|--------|
| Ctrl+S / Cmd+S | Save to file |
| Ctrl+O / Cmd+O | Open file (disabled in readonly) |
| Ctrl+Z / Cmd+Z | Undo |
| Ctrl+Y / Cmd+Y | Redo |
| Ctrl+Shift+Z / Cmd+Shift+Z | Redo (alternative) |
| Ctrl+A / Cmd+A | Select all |
| Ctrl+C / Cmd+C | Copy |
| Ctrl+X / Cmd+X | Cut |
| Ctrl+V / Cmd+V | Paste |
| Ctrl+← / Cmd+← | Move cursor to previous word |
| Ctrl+→ / Cmd+→ | Move cursor to next word |
| Ctrl+Shift+← / Cmd+Shift+← | Select to previous word |
| Ctrl+Shift+→ / Cmd+Shift+→ | Select to next word |
| Enter | Expand collapsed node at cursor |
| Shift+Enter | Collapse innermost node containing cursor |
| Tab | Navigate to next attribute (key or value) |
| Shift+Tab | Navigate to previous attribute |
| Home | Go to start of line |
| End | Go to end of line |
| Ctrl+Home / Cmd+Home | Go to start of document |
| Ctrl+End / Cmd+End | Go to end of document |
| PageUp | Scroll up one page |
| PageDown | Scroll down one page |
| Shift+Home/End/PageUp/PageDown | Extend selection while navigating |
| Ctrl+I / Cmd+I | Add feature via prompt (requires internal-add-shortcut attribute) |
Overriding Shortcuts
All keyboard shortcuts handled by the editor call stopPropagation() to prevent them from bubbling up. To override a default shortcut behavior, use a capture-phase event listener:
editor.addEventListener('keydown', async (e) => {
// Override Ctrl+S to save to a remote server instead of downloading
if (e.ctrlKey && e.key === 's') {
e.preventDefault();
e.stopPropagation();
await saveToRemoteServer(editor.getAll());
}
// Override Ctrl+O to load from a remote API instead of local filesystem
if (e.ctrlKey && e.key === 'o') {
e.preventDefault();
e.stopPropagation();
const features = await openRemoteFeatureSelector();
if (features) editor.set(features);
}
}, { capture: true }); // capture: true is required to intercept before the editorEvents
change
Fired when content changes and GeoJSON is valid (debounced 150ms).
editor.addEventListener('change', (e) => {
console.log(e.detail); // Parsed GeoJSON object directly
});Event detail: The parsed GeoJSON object (always a FeatureCollection).
Note: Hidden features (toggled via the eye icon) are automatically excluded from the emitted GeoJSON. This allows temporary filtering without modifying the actual JSON content.
Example:
// User edits features only, but change event includes the FeatureCollection wrapper
editor.addEventListener('change', (e) => {
console.log(e.detail);
// → { type: "FeatureCollection", features: [{ type: "Feature", ... }] }
});error
Fired when content changes but JSON is invalid or GeoJSON validation fails (debounced 150ms).
editor.addEventListener('error', (e) => {
console.error(e.detail.error); // Error message
console.log(e.detail.errors); // Array of validation errors (GeoJSON validation only)
console.log(e.detail.content); // Raw content for debugging
});Event detail properties:
| Property | Type | Description |
|----------|------|-------------|
| error | string | Error message (JSON parse error or GeoJSON validation summary) |
| errors | string[] | Array of validation errors with paths (GeoJSON validation only) |
| content | string | Raw editor content (for debugging) |
GeoJSON validation errors include:
- Invalid types (e.g.,
"LinearRing") - Unknown types (any
typevalue not in the GeoJSON specification)
current-features
Fired when the current feature(s) change. Useful for synchronizing the editor with a map display. Emits a FeatureCollection containing all features that overlap with the cursor position or selection.
Triggers:
- Editor gains focus → emits FeatureCollection with feature at cursor
- Cursor moves to a different feature → emits FeatureCollection with new feature(s)
- Selection spans multiple features → emits FeatureCollection with all overlapping features
- Cursor moves outside any feature → emits empty FeatureCollection
- Editor loses focus → emits empty FeatureCollection
editor.addEventListener('current-features', (e) => {
const featureCollection = e.detail; // Always a FeatureCollection
if (featureCollection.features.length > 0) {
// Highlight these features on your map
highlightLayer.setData(featureCollection);
} else {
// No current features
highlightLayer.setData({ type: 'FeatureCollection', features: [] });
}
});Event detail: A FeatureCollection object containing:
- All features overlapping with the current selection (if selection exists)
- The single feature at cursor position (if no selection)
- Empty features array if cursor is outside any feature or editor loses focus
Note: The event is only fired when the set of features changes, not on every cursor movement within the same feature. This prevents excessive event firing during normal editing.
Styling
The component uses Shadow DOM with CSS variables for theming. Customize colors via CSS custom properties (see Theme Control).
Browser Support
Works in all modern browsers supporting:
- Web Components
- Shadow DOM
- ES6 Modules
Development
See DEVELOPMENT.md for detailed development guide.
# Install dependencies
npm install
# Start dev server with live demo
npm run dev
# Build for production
npm run buildLicense
MIT
