@nextgis/control-container is a universal library for creating and managing map controls layout.
It provides a flexible system for placing controls in map corners, creating grouped toolbars, nested panels, and switchable controls.
The library can be used standalone with any map engine and also serves as the foundation for framework-specific integrations:
@nextgis/leaflet-control-container@nextgis/maplibre-gl-control-container@nextgis/ol-control-containerThis allows using the same control layout patterns and APIs across different map engines.
╭───────────────────────────────────────────╮
│ top-left top-right │
│ MAP │
│ bottom-left bottom-right │
╰───────────────────────────────────────────╯
npm install @nextgis/control-container
import ControlContainer, {
createButtonControl,
createControl,
createControlContainer,
createToggleControl,
} from '@nextgis/control-container';
const controlContainer = new ControlContainer({
map,
});
controlContainer.addTo('#map');
Available positions:
top-lefttop-rightbottom-leftbottom-rightcontrolContainer.append('<div>Top right</div>', 'top-right');
controlContainer.append(
'<div class="map-label">Map tools</div>',
'top-right',
);
const element = document.createElement('div');
element.textContent = 'Custom element';
controlContainer.append(element, 'top-right');
The order option controls the order of controls in the same position or panel.
controlContainer.append('<div>First</div>', 'top-right', 0);
controlContainer.append('<div>Second</div>', 'top-right', 10);
For controls, pass order in the third argument.
controlContainer.addControl(firstControl, 'top-right', {
order: 0,
});
controlContainer.addControl(secondControl, 'top-right', {
order: 10,
});
Controls are attached to the map edge by default. Controls created with
bar: true use the standard outer offset automatically.
Set margin: false to keep a bar control attached to the map edge. This also
works for button and toggle controls.
const panel = createControl(
{
onAdd() {
return undefined;
},
onRemove() {},
},
{
bar: true,
margin: false,
},
);
A control is an object with an onAdd method. The method returns an HTML element.
const customControl = {
onAdd() {
const container = document.createElement('div');
container.textContent = 'Custom control';
return container;
},
onRemove() {},
};
controlContainer.addControl(customControl, 'bottom-right');
Use createButtonControl for simple button controls.
const button = createButtonControl({
title: 'Action',
html: 'Run',
onClick() {
console.log('Clicked');
},
});
controlContainer.addControl(button, 'top-left');
Use createControl to create a panel for other controls.
A panel can be registered with an id when it is added to the control container. After that, other controls can be placed inside it with { inside: id }.
const panel = createControl(
{
onAdd() {
return undefined;
},
onRemove() {},
},
{
orientation: 'vertical',
gap: 6,
margin: true,
},
);
controlContainer.addControl(panel, 'top-right', {
id: 'tools-panel',
order: 20,
});
Then add controls inside the panel.
const button = createButtonControl({
title: 'Button',
html: 'Button',
onClick() {
console.log('Button clicked');
},
});
controlContainer.addControl(button, { inside: 'tools-panel' }, {
order: 10,
});
const horizontalPanel = createControl(
{
onAdd() {
return undefined;
},
onRemove() {},
},
{
orientation: 'horizontal', // 'vertical'
gap: 0,
},
);
controlContainer.addControl(horizontalPanel, 'top-right', {
id: 'orientated-panel',
order: 10,
});
Switch controls allow only one active control in the same group and target container.
const select = createToggleControl({
html: {
on: 'Select on',
off: 'Select',
},
switch: 'tools',
onClick(status) {
console.log(status);
},
});
const edit = createToggleControl({
html: {
on: 'Edit on',
off: 'Edit',
},
switch: 'tools',
onClick(status) {
console.log(status);
},
});
controlContainer.addControl(select, 'top-left');
controlContainer.addControl(edit, 'top-left');
const panel = createControl(
{
onAdd() {
return undefined;
},
onRemove() {},
},
{
orientation: 'vertical',
gap: 4,
},
);
controlContainer.addControl(panel, 'top-right', {
id: 'switch-panel',
});
const identify = createToggleControl({
html: {
on: 'Identify on',
off: 'Identify',
},
switch: 'panel-tools',
});
const measure = createToggleControl({
html: {
on: 'Measure on',
off: 'Measure',
},
switch: 'panel-tools',
});
controlContainer.addControl(identify, { inside: 'switch-panel' }, {
order: 10,
});
controlContainer.addControl(measure, { inside: 'switch-panel' }, {
order: 20,
});
Use different switch names for independent groups.
const draw = createToggleControl({
html: 'Draw',
switch: 'edit-tools',
});
const erase = createToggleControl({
html: 'Erase',
switch: 'edit-tools',
});
const info = createToggleControl({
html: 'Info',
switch: 'view-tools',
});
disableOnSecondClickconst measure = createToggleControl({
html: {
on: 'Measure on',
off: 'Measure',
},
switch: 'tools',
disableOnSecondClick: true,
});
panel.onSwitchChange((event) => {
console.log(event.group);
console.log(event.status);
console.log(event.activeControl);
});
const asyncControl = createToggleControl({
html: {
on: 'Enabled',
off: 'Disabled',
},
switch: 'layers',
async onClick(status) {
await Promise.resolve(status);
},
});
Need to fix a bug or add a feature to @nextgis/control-container? We provide custom development and support for this software. Contact us to discuss options!