UI Widgets in Earth Engine

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

  1. What event does a slider use: onClick or onSlide?
  2. How do you create a dropdown menu?
  3. What is the purpose of ui.Panel?
  4. How do you add a panel to the map interface?

Next steps