Earth Engine's UI library lets you build interactive applications with buttons, sliders, dropdowns, and more. This module covers the core widgets you need to create user-friendly apps.
Learning objectives
- Create buttons, sliders, textboxes, and select widgets.
- Handle widget events (onClick, onChange).
- Organize widgets in panels with layouts.
- Update the map based on user interactions.
Why it matters
Interactive controls let non-coders explore your analysis. A slider to change thresholds, a dropdown to select dates—these turn static scripts into powerful tools anyone can use.
Key vocabulary
- Widget
- An interactive UI element (button, slider, label, etc.).
- Panel
- A container that holds and organizes multiple widgets.
- Callback
- A function that runs when a widget event occurs (click, change).
- ui.root
- The root container for all UI elements in an Earth Engine app.
Quick win: Interactive NDVI threshold
This example creates a slider that updates the NDVI threshold in real-time:
// Load a Landsat image
var image = ee.Image('LANDSAT/LC08/C02/T1_L2/LC08_044034_20210623')
.multiply(0.0000275).add(-0.2);
var ndvi = image.normalizedDifference(['SR_B5', 'SR_B4']).rename('NDVI');
// Center and add base layer
Map.centerObject(image, 10);
Map.addLayer(ndvi, {min: 0, max: 0.8, palette: ['brown', 'yellow', 'green']}, 'NDVI');
// Create a slider widget
var slider = ui.Slider({
min: 0,
max: 1,
value: 0.3,
step: 0.05,
style: {width: '300px'}
});
// Create a label to show current value
var label = ui.Label('Threshold: 0.3');
// Define callback function
var updateThreshold = function(value) {
// Update label
label.setValue('Threshold: ' + value.toFixed(2));
// Remove old mask layer
var layers = Map.layers();
if (layers.length() > 1) {
layers.remove(layers.get(1));
}
// Create and add new mask
var mask = ndvi.gte(value);
Map.addLayer(mask.updateMask(mask), {palette: ['00ff00']}, 'NDVI >= ' + value);
};
// Connect slider to callback
slider.onSlide(updateThreshold);
// Create panel and add to map
var panel = ui.Panel({
widgets: [ui.Label('NDVI Threshold Selector'), slider, label],
style: {position: 'top-left', padding: '8px'}
});
Map.add(panel);
What you should see
A panel with a slider appears on the map. Moving the slider updates the green mask showing pixels above the selected NDVI threshold.
Widget reference
| Widget | Purpose | Key Event |
|---|---|---|
ui.Label() |
Display text | — |
ui.Button() |
Trigger actions | onClick() |
ui.Slider() |
Select numeric value | onSlide() |
ui.Textbox() |
Text input | onChange() |
ui.Select() |
Dropdown menu | onChange() |
ui.Checkbox() |
Toggle option | onChange() |
ui.DateSlider() |
Select date range | onChange() |
ui.Chart |
Display charts | — |
More examples
Button
var button = ui.Button({
label: 'Run Analysis',
onClick: function() {
print('Button clicked!');
// Add your analysis code here
}
});
Map.add(button);
Select (Dropdown)
var select = ui.Select({
items: ['Landsat 8', 'Landsat 9', 'Sentinel-2'],
value: 'Landsat 8',
onChange: function(selected) {
print('Selected:', selected);
}
});
Map.add(select);
Textbox
var textbox = ui.Textbox({
placeholder: 'Enter coordinates...',
onChange: function(text) {
print('User entered:', text);
}
});
Map.add(textbox);
DateSlider
var dateSlider = ui.DateSlider({
start: '2020-01-01',
end: '2023-12-31',
value: '2023-06-01',
onChange: function(range) {
print('Date range:', range.start(), range.end());
}
});
Map.add(dateSlider);
Panel layouts
Use panels to organize widgets together:
// Vertical panel (default)
var verticalPanel = ui.Panel({
widgets: [
ui.Label('Title'),
ui.Slider({min: 0, max: 100}),
ui.Button('Submit')
],
layout: ui.Panel.Layout.flow('vertical')
});
// Horizontal panel
var horizontalPanel = ui.Panel({
widgets: [
ui.Button('Option A'),
ui.Button('Option B'),
ui.Button('Option C')
],
layout: ui.Panel.Layout.flow('horizontal')
});
// Add panel to map sidebar
ui.root.insert(0, verticalPanel);
Pro tips
- Use
ui.root.clear()+ui.root.add()for full-screen apps. - Style widgets with
style: {width: '200px', color: 'red'}. - Use
widget.setDisabled(true)to prevent interaction during processing. - Nest panels for complex layouts (sidebar with sections).
- Test on different screen sizes before publishing.
Try it: Build a layer toggle
Create checkboxes that show/hide different map layers:
// Add layers
Map.addLayer(ndvi, {min:0, max:0.8, palette:['brown','green']}, 'NDVI', true);
Map.addLayer(image, {bands:['SR_B4','SR_B3','SR_B2'], min:0, max:0.3}, 'RGB', false);
// Create checkboxes
var ndviCheck = ui.Checkbox('Show NDVI', true);
ndviCheck.onChange(function(checked) {
Map.layers().get(0).setShown(checked);
});
var rgbCheck = ui.Checkbox('Show RGB', false);
rgbCheck.onChange(function(checked) {
Map.layers().get(1).setShown(checked);
});
Map.add(ui.Panel([ndviCheck, rgbCheck]));
Common mistakes
- Forgetting to connect the callback function (
onClick,onSlide). - Using client-side loops inside server-side callbacks.
- Not removing old layers before adding new ones (layer pile-up).
- Making panels too wide for the map area.
- Hardcoding values instead of reading from widgets.
Quick self-check
- What event does a slider use:
onClickoronSlide? - How do you create a dropdown menu?
- What is the purpose of
ui.Panel? - How do you add a panel to the map interface?
Next steps
- Engaging with the Public - overview and animations
- Science Communication Guide
- Lab 20: Deploying GEE Apps