Skip to main content

@deck.gl/widgets

Widgets are UI components around the WebGL2/WebGPU canvas to offer controls and information for a better user experience.

This module contains the following widgets:

Geospatial Widgets

View Widgets

Information Widgets

Control Widgets

Utility Widgets

Installation

Install from NPM

npm install deck.gl
# or
npm install @deck.gl/core @deck.gl/widgets
import {FullscreenWidget} from '@deck.gl/widgets';
import '@deck.gl/widgets/stylesheet.css';

new FullscreenWidget();

Include the Standalone Bundle

<script src="https://unpkg.com/deck.gl@^9.0.0/dist.min.js"></script>
<link href="https://unpkg.com/deck.gl@^9.0.0/dist/stylesheet.css" rel='stylesheet' />
<!-- or -->
<script src="https://unpkg.com/@deck.gl/core@^9.0.0/dist.min.js"></script>
<script src="https://unpkg.com/@deck.gl/widgets@^9.0.0/dist.min.js"></script>
<link href="https://unpkg.com/@deck.gl/widgets@^9.0.0/dist/stylesheet.css" rel='stylesheet' />

Using Widgets

import {Deck} from '@deck.gl/core';
import {
CompassWidget,
ZoomWidget,
FullscreenWidget,
ScreenshotWidget,
} from '@deck.gl/widgets';
import '@deck.gl/widgets/stylesheet.css';

new Deck({
initialViewState: INITIAL_VIEW_STATE,
controller: true,
layers: [
...
],
widgets: [
new ZoomWidget(),
new CompassWidget(),
new FullscreenWidget(),
new ScreenshotWidget()
]
});

The built-in widgets support both dark and light color scheme changes and can be wired up to dynamically respond to color scheme changes like so:

import {Deck} from '@deck.gl/core';
import {
CompassWidget,
ZoomWidget,
FullscreenWidget,
ScreenshotWidget,
DarkGlassTheme,
LightGlassTheme
} from '@deck.gl/widgets';
import '@deck.gl/widgets/stylesheet.css';

/* global window */
const prefersDarkScheme = window.matchMedia('(prefers-color-scheme: dark)');
const widgetTheme = prefersDarkScheme.matches ? DarkGlassTheme : LightGlassTheme;

new Deck({
initialViewState: INITIAL_VIEW_STATE,
controller: true,
layers: [ ... ],
widgets: [
new ZoomWidget({style: widgetTheme}),
new CompassWidget({style: widgetTheme}),
new FullscreenWidget({style: widgetTheme}),
new ScreenshotWidget({style: widgetTheme})
]
});

Using with Multiple Views

Widgets with UI (e.g. a button or panel) can be positioned relative to the deck.gl view they are controlling, via the viewId and placement props. See WidgetProps.

The viewId controls which HTML container will mount to, and the placement prop will position it relative to the container it is in, like so:

new Deck({
views:[
new MapView({id: 'left-map'}),
new MapView({id: 'right-map'})
],
widgets: [
new FullscreenWidget({placement: 'top-right'}),
new ZoomWidget({viewId: 'left-map'}),
new GimbalWidget({viewId: 'right-map'}),
]
})

This configuration will result in the following HTML structure:

<!-- map container -->
<div class="deck-widget-container">
<canvas id="deckgl-overlay">
<!-- size of full map container -->
<div>
<div class="top-right">
</FullscreenWidget>
</div>
</div>
<!-- size and position of the "left-map" view -->
<div>
<div class="top-left">
</ZoomWidget>
</div>
</div>
<!-- size and position of the "right-map" view -->
<div>
<div class="top-left">
</GimbalWidget>
</div>
</div>
</div>

Remarks:

  • Widgets in the default container will be overlapped by view-specific widgets.
  • Widget UI with dynamic positioning, such as an InfoWidget, may not expose the placement prop as they control positioning internally.
  • For more information about using multiple deck.gl views, see the Using Multiple Views guide.

Controlled vs Uncontrolled Mode

Many deck.gl widgets support both controlled and uncontrolled modes, similar to React form components.

Uncontrolled Mode (Default)

By default, widgets manage their own internal state. You can optionally provide an initial value and receive callbacks when the state changes:

new ThemeWidget({
initialThemeMode: 'light',
onThemeModeChange: (mode) => console.log('Theme changed:', mode)
})

Controlled Mode

When you provide a state prop (e.g., themeMode, fullscreen, time), the widget enters controlled mode. In this mode, the widget's state is driven entirely by the prop value, and you must update it via callbacks:

let themeMode = 'light';

new ThemeWidget({
themeMode,
onThemeModeChange: (mode) => {
themeMode = mode;
deck.setProps({widgets: [new ThemeWidget({themeMode, onThemeModeChange: ...})]});
}
})

Reading Widget State

Widgets with internal state expose getter methods (e.g., getThemeMode(), getFullscreen()) that return the current state regardless of whether the widget is controlled or uncontrolled.

Writing new Widgets

A widget should inherit the Widget class. Here is a custom widget that shows a spinner while layers are loading:

import {Deck, Widget} from '@deck.gl/core';

class LoadingIndicator extends Widget {
element?: HTMLDivElement;
size: number;

constructor(options: {
size: number;
}) {
this.size = options.size;
}

onRenderHTML(el: HTMLElement) {
el.className = 'spinner';
el.style.width = `${this.size}px`;
// TODO - create animation for .spinner in the CSS stylesheet
}

onRedraw({layers}) {
const isVisible = layers.some(layer => !layer.isLoaded);
this.rootElement.style.display = isVisible ? 'block' : 'none';
}
}

new Deck({
widgets: [new LoadingIndicator({size: 48})]
});

Themes and Styling

deck.gl widget appearance can be customized using themes and CSS.