Install the library and its host-owned peers in your React application:
pnpm add @georeferencing/core @georeferencing/react @georeferencing/plugins react@19 react-dom@19 ol@10
npm and Yarn work the same way (npm install …, yarn add …). The verified peer ranges are React/React DOM >=19.3.0 <20 and OpenLayers >=10.10.0 <11. Install the same version of all @georeferencing/* packages; see keep package versions aligned.
Core and React require no export plugins. The example opts into three formats; omit exports and the plugins dependency for alignment and saving alone. See optional export plugins.
Create one controller and engine for each editor session. Keep them stable across renders. The map must already have a visible target and an initialized view. Select a working CRS appropriate to your data; it may differ from the map projection.
import { createWorkerEngine } from "@georeferencing/core/engine";
import { geoTiff } from "@georeferencing/plugins/geotiff";
import { jpeg } from "@georeferencing/plugins/jpeg";
import { pdf } from "@georeferencing/plugins/pdf";
import {
type ControllerOptions,
Georeferencer,
GeoreferencerController,
} from "@georeferencing/react";
import type OLMap from "ol/Map.js";
import "@georeferencing/react/styles.css";
export function createEditor(
workingCrs: string,
persistFeatures: NonNullable<ControllerOptions["onSave"]>,
persistDraft?: ControllerOptions["onSaveDraft"],
) {
const engine = createWorkerEngine();
const controller = new GeoreferencerController({
workingCrs,
engine,
digitizing: true,
exports: [geoTiff(), jpeg(), pdf()],
onSave: persistFeatures,
onSaveDraft: persistDraft,
});
return {
controller,
dispose() {
controller.dispose();
engine.dispose();
},
};
}
export function ImageEditor({
map,
controller,
}: {
map: OLMap;
controller: GeoreferencerController;
}) {
return <Georeferencer controller={controller} referenceMap={map} />;
}
The host calls createEditor once for a session and renders ImageEditor with its existing map and the returned controller. The save functions in this example are injected host services, not simulated persistence. Resolve them only after your storage has accepted the snapshot.
The component attaches its own OpenLayers binding. On unmount it detaches package layers/interactions and suspends processing while retaining the session. When the host permanently closes the session, unmount the editor and then call the returned dispose. Do not permanently dispose retained resources during React Strict Mode's temporary effect cleanup.
The stylesheet is optional and scoped. The host sets the map element's dimensions and imports ol/ol.css for its own map controls.
The ImagePanel, GcpPanel, AlignmentPanel, ReferencePanel and FeaturePanel components share the same controller. useGeoreferencer(controller) subscribes to its snapshot without taking resource ownership. Use attachReferenceMap once, call its detach() during cleanup, and call controller.start()/controller.suspend() at your component lifecycle boundaries. Configure a guard callback for unsaved changes: composable panels do not install the ready-made editor's dialog.
Pass t(message) to translate default English UI text. The ready-made editor also accepts formatError for error-code localization and propertyEditor(feature, update) for a host-specific property form. Call update with replacement JSON properties; frozen feature snapshots must not be mutated.
See lifecycle and saving before integrating persistence, and worker deployment before building your production bundle.
@georeferencing/core, @georeferencing/plugins and @georeferencing/react are
released together with identical version numbers. Always install the same
version of every @georeferencing/* package your application uses, and upgrade
them together:
# Replace <version> with one release number, for example when upgrading:
pnpm add @georeferencing/core@<version> @georeferencing/plugins@<version> @georeferencing/react@<version>
Plugins and React declare core as a regular dependency. When the versions differ,
your package manager can install a second copy of core, which then also bundles
its own proj4 projection registry. The host's controller, map binding and export
plugins would no longer share registered projections or datum grids, and the
application ships the processing code twice. Check for a single copy with
pnpm why @georeferencing/core (or npm ls @georeferencing/core); a pnpm
overrides entry or npm overrides field can force one version if a transitive
dependency pulls in another.
The Hamburg demo
starts with previewMode: "manual" on the core controller and passes the host map
view as Georeferencer.referenceView. Pick an image location, pick its map match,
and repeat. Run alignment fits and previews those pairs; export and optional
accepted drawing are separate actions.
Core defaults to "automatic" for existing integrations. Change the policy with
controller.setPreviewMode(mode); it is transient configuration outside session
JSON. Automatic mode waits for enough enabled pairs before fitting; manual mode
also applies to undo/redo, restoration and remount. Neither mode bypasses worker
rank/domain checks or stale-revision protection. The React PreviewControls
component exposes this setting and a run/review button for custom layouts.
The guided layout announces the next endpoint, activates matching after load, keeps both viewers visible on desktop, and uses Image/Map buttons below 720 px. Edits invalidate the old preview and acceptance; manual mode requires another run. The supplied WebP is decoded locally with the same size/orientation/memory checks as other supported input formats.