Create a Dashboard with deephaven.ui
This guide shows you how to build a dashboard with deephaven.ui, Deephaven’s Python library to create user interfaces. You’ll use a wide range of components supported by the library to familiarize you with what deephaven.ui provides and dive deep into simulated data with live dataframes, visualizations, and interactivity seamlessly integrated into a dashboard.
- First, you’ll learn the basic 
deephaven.uicomponents. Basic components use panels, which are individual and adjustable windows within Deephaven. Tables, charts, and other components are rendered in panels. - After creating panels, you’ll create a dashboard. A dashboard is a collection of panels that open in a separate window within Deephaven.
 - Then, you’ll create a custom panel component. Custom components enable rich interactivity within your panels and dashboards.
 - Finally, you’ll embed your custom component into your dashboard.
 
To follow along, you need the deephaven.ui package and simulated data and charts from deephaven.plot.express. Both of these packages are included in the default setup.
Import the simulated iris data with this script:
from deephaven import ui
import deephaven.plot.express as dx
iris = dx.data.iris()

In this dataset, the Species column is a categorical column with three values: setosa, Versicolor, and Virginia. The SepalLength, SepalWidth, PetalLength, and PetalWidth columns are continuous numerical columns that contain measurements of the sepal and petal of an iris flower. The Timestamp column is also useful for ordering the data.
You’ll mostly focus on SepalLength and SepalWidth in this guide.
Basic components
Components are the building blocks of deephaven.ui. Each component takes parameters that control how the component appears. By default, a component renders in a panel.
ui.table
Wrapping a table in ui.table unlocks visual functionality on the table.
Since you’re investigating SepalLength and SepalWidth, create a ui.table that accentuates the latest filtered data.
With iris, create a ui.table that:
- Reverses the order so that the newest rows are shown first.
 - Pulls the 
Speciescolumn to the front along withTimestamp. - Hides the 
PetalLength,PetalWidth, andSpeciesIDcolumns. - Uses the compact table density so you can see as many rows as possible.
 
ui_iris = ui.table(
  iris,
  reverse=True,
  front_columns=["Timestamp", "Species"],
  hidden_columns=["PetalLength", "PetalWidth", "SpeciesID"],
  density="compact"
)

Charts
Charts from Deephaven Plotly Express (dx) have no deephaven.ui specific wrapping and are added directly. Create a dx.scatter chart that compares SepalLength and SepalWidth by Species.
scatter_by_species = dx.scatter(iris, x = "SepalLength", y = "SepalWidth", by="Species")

ui.text
The ui.text component adds basic text. Create text to accompany the chart and table.
sepal_text = ui.text("SepalLength vs. SepalWidth By Species")

ui.flex
Wrap your chart and ui.table in a ui.flex component. ui.flex is an implementation of Flexbox that enables responsive layouts that adjust as you resize panels.
Items within a ui.flex component stretch and shrink based on available space.
sepal_flex = ui.flex(ui_iris, scatter_by_species)

The direction of sepal_flex is "row". Add sepal_text and sepal_flex to another panel, with a direction of "column".
sepal_flex_column = ui.flex(sepal_text, sepal_flex, direction="column")

Tabs
The ui.tabs component enables tabs within a panel. Create histograms of SepalLength to display in tabs.
Histograms are useful to display comparisons of data distributions, so create dx.histogram charts of the columns of interest, SepalLength and SepalWidth, by Species.
Create ui.tab elements for sepal_flex, sepal_length_hist, and sepal_width_hist, then pass them to ui.tabs to switch between different views.
sepal_length_hist = dx.histogram(iris, x="SepalLength", by="Species")
sepal_width_hist = dx.histogram(iris, x="SepalWidth", by="Species")
sepal_tabs = ui.tabs(
    ui.tab(sepal_flex, title="Sepal Length vs. Sepal Width"),
    ui.tab(sepal_length_hist, title="Sepal Length Histogram"),
    ui.tab(sepal_width_hist, title="Sepal Width Histogram"),
)
sepal_flex_tabs = ui.flex(sepal_text, sepal_tabs, direction="column")

Markdown
ui.markdown components allow you to provide text in a markdown format. Create an informational panel with markdown text.
about_markdown = ui.markdown(r"""
### Iris Dashboard
Explore **SepalLength** and **SepalWidth** from the Iris dataset with **deephaven.ui**
- The data powering this dashboard is simulated Iris data
- Charts are from Deephaven Plotly Express
- Other components are from **deephaven.ui**
""")

Now you have two responsive panels with fundamental components of deephaven.ui that explore SepalLength and SepalWidth by Species.
Dashboard
By default, components are rendered in a panel, but a dashboard enables more complex layouts in an isolated window.
When you have multiple panels, you can create a dashboard to contain them so that you can add even more panels later for your iris data exploration.
Expand for complete code up to this point
from deephaven import ui
import deephaven.plot.express as dx
from deephaven import agg
iris = dx.data.iris()
ui_iris = ui.table(
    iris,
    reverse=True,
    front_columns=["Timestamp", "Species"],
    hidden_columns=["PetalLength", "PetalWidth", "SpeciesID"],
    density="compact",
)
scatter_by_species = dx.scatter(iris, x="SepalLength", y="SepalWidth", by="Species")
sepal_text = ui.text("SepalLength vs. SepalWidth By Species Panel")
sepal_flex = ui.flex(ui_iris, scatter_by_species)
sepal_flex_column = ui.flex(sepal_text, sepal_flex, direction="column")
sepal_length_hist = dx.histogram(iris, x="SepalLength", by="Species")
sepal_width_hist = dx.histogram(iris, x="SepalWidth", by="Species")
sepal_tabs = ui.tabs(
    ui.tab(sepal_flex, title="Sepal Length vs. Sepal Width"),
    ui.tab(sepal_length_hist, title="Sepal Length Histogram"),
    ui.tab(sepal_width_hist, title="Sepal Width Histogram"),
)
sepal_flex_tabs = ui.flex(sepal_text, sepal_tabs, direction="column")
about_markdown = ui.markdown(r"""
### Iris Dashboard
Explore the Iris dataset with **deephaven.ui**
- The data powering this dashboard is simulated Iris data
- Charts are from Deephaven Plotly Express
- Other components are from **deephaven.ui**
  """)
Before that, create a ui.panel manually to provide a title.
sepal_panel = ui.panel(sepal_flex_tabs, title="Sepal Panel")
iris_dashboard = ui.dashboard(sepal_panel)

You now have a one-panel dashboard.
You can create a default layout with multiple panels. Create a panel for about_markdown so you can give it a title.
about_panel = ui.panel(about_markdown, title="About")

Row
One way to create a default layout is by wrapping your panels in a ui.row component.
iris_dashboard_row = ui.dashboard(ui.row(about_panel, sepal_panel))

Column
Another way to create a default layout is with ui.column.
iris_dashboard_column = ui.dashboard(ui.column(about_panel, sepal_panel))

Stack
One more way to create a default layout is with ui.stack. The last panel is active by default.
ui.stack is useful if you want a tabbed layout but also the ability to move the individual components around, which is not possible with ui.tabs.
Create tabs that show the average, max, and min values of SepalLength, SepalWidth, PetalLength, and PetalWidth by Species.
You can compare a specific statistic across the different Species within the same panel or move between panels to compare different statistics across Species.
from deephaven import agg
iris_avg = iris.agg_by([agg.avg(cols=["SepalLength", "SepalWidth", "PetalLength", "PetalWidth"])], by=["Species"])
iris_max = iris.agg_by([agg.max_(cols=["SepalLength", "SepalWidth", "PetalLength", "PetalWidth"])], by=["Species"])
iris_min = iris.agg_by([agg.min_(cols=["SepalLength", "SepalWidth", "PetalLength", "PetalWidth"])], by=["Species"])
ui_iris_avg = ui.panel(iris_avg, title="Average")
ui_iris_max = ui.panel(iris_max, title="Max")
ui_iris_min = ui.panel(iris_min, title="Min")
iris_agg_stack = ui.stack(ui_iris_avg, ui_iris_max, ui_iris_min)
  
iris_dashboard_stack = ui.dashboard(iris_agg_stack)

Interactivity
So far, you’ve worked with deephaven.ui components that don’t interact with each other. Now, you’ll create your own component with interactivity and embed it into your dashboard.
Since this walkthrough investigates SepalLength and SepalWidth, this section shows you how to create a dx.densityheatmap chart that shows the density of SepalLength and SepalWidth, with a Species filter.
ui.component
A custom component uses the ui.component decorator. The decorator signals to the deephaven.ui rendering engine that this component needs to be rendered. Create a function with the ui.component decorator that returns "Hello, World!".
@ui.component
def custom_component():
  return "Hello, World!"
custom_panel = custom_component()

Picker
A ui.picker allows you to select options from a list.
@ui.component
def species_panel():
  species_picker = ui.picker("setosa", "versicolor", "virginica")
  return species_picker
picker_panel = species_panel()

Table-backed ui.picker
ui.pickers can pull directly from a table so they update automatically based on a column in the table. Modify your ui.picker to pass in a table instead, which is recommended for dynamic data.
Note
It’s important to filter your table down to the distinct values you want. The ui.picker does not do this for you.
species_table = iris.view("Species").select_distinct()
@ui.component
def species_panel():
  species_picker = ui.picker(species_table)
  return species_picker
species_picker_panel = species_panel()

ui.use_state
The ui.use_state hook is how you’ll enable interactivity. The hook takes a starting value and returns a tuple of the current value and a function to set the value.
Next, modify your picker to take the species and set_species from the hook you just defined using on_change and selected_key. on_change is called whenever an option is selected. selected_key is the currently selected option. Using these makes the component controlled, meaning that the selected_key is controlled outside of the ui.picker itself. This allows you to use the selected_key in other ways such as filtering a table.
Warning
Hooks like ui.use_state are only available in components decorated with ui.component.
Additionally, hooks should not be called conditionally within a component. Create a new component if conditional rendering is needed.
@ui.component
def species_panel():
  species, set_species = ui.use_state()
  species_picker = ui.picker(species_table, on_change=set_species, selected_key=species, label="Current Species")
  return species_picker
species_picker_panel = species_panel()

Now, your picker is controlled by the rendering engine and updates as you pick values, and you have an up-to-date value from the table to filter on.
Utilizing State
Now, add a table filtered on this value and a chart that uses this filtered table. Return them with the picker.
You’ll create a dx.heatmap, which shows the joint density of SepalLength and SepalWidth (the variables of interest) filtered by Species.
@ui.component
def species_panel():
  species, set_species = ui.use_state()
  species_picker = ui.picker(
    species_table,
    on_change=set_species,
    selected_key=species,
    label="Current Species"
  )
  filtered_table = iris.where("Species = species")
  heatmap = dx.density_heatmap(filtered_table, x="SepalLength", y="SepalWidth")
  
  return ui.panel(ui.flex(species_picker, heatmap, direction="column"), title="Investigate Species")
species_picker_panel = species_panel()

ui.illustrated_message
Currently, an empty table and chart appear if no species is selected. For a more user-friendly experience, add a ui.illustrated_message component to display instead if no species is selected.
@ui.component
def species_panel():
  species, set_species = ui.use_state()
  species_picker = ui.picker(
    species_table,
    on_change=set_species,
    selected_key=species,
    label="Current Species"
  )
  heatmap = ui.illustrated_message(
      ui.icon("vsFilter"),
      ui.heading("Species required"),
      ui.content("Select a species to display filtered table and chart."),
      width="100%",
  )
  if species:
    filtered_table = iris.where("Species = species")
    heatmap = dx.density_heatmap(filtered_table, x="SepalLength", y="SepalWidth")
  return ui.panel(ui.flex(species_picker, heatmap, direction="column"), title="Investigate Species")
species_picker_panel = species_panel()

Utilizing custom components
Next, embed your custom component in your dashboard.
iris_species_dashboard = ui.dashboard(
    ui.column(
        ui.row(about_panel, iris_agg_stack), ui.row(sepal_panel, species_picker_panel)
    )
)

The top row contains lots of empty space. Resize the height of the top row to 1 and the bottom row to 2 for a ratio of 1:2, so the bottom row is twice the height of the top row.
iris_species_dashboard_resized = ui.dashboard(ui.column(ui.row(about_panel, iris_agg_stack, height=1), ui.row(sepal_panel, species_picker_panel, height=2)))

Dashboard state across panels
Currently, the ui.picker selects Species within a panel. You can change state across panels as well.
Recreate the sepal_flex_tabs panel within the create_sepal_panel function, which takes a set_species function as an argument. Now, we’ll add a ui.table event – these are useful if you see a row you want to investigate further.
- Add a listener (
on_row_double_press) to the table that is called when a row is double-clicked, returning the row data. This sets theSpeciesvalue displayed in theui.pickeranddx.density_heatmap. - Pull the 
Speciesvalue from the row data and set it withset_species. - Then, create 
sepal_panelwithcreate_sepal_panel, passingset_speciesto it. 
def create_sepal_panel(set_species):
    ui_iris = ui.table(
        iris,
        reverse=True,
        front_columns=["Timestamp", "Species"],
        hidden_columns=["PetalLength", "PetalWidth", "SpeciesID"],
        density="compact",
        on_row_double_press=lambda event: set_species(event["Species"]["value"]),
    )
    sepal_flex = ui.flex(ui_iris, scatter_by_species)
    sepal_tabs = ui.tabs(
        ui.tab(sepal_flex, title="Sepal Length vs. Sepal Width"),
        ui.tab(sepal_length_hist, title="Sepal Length Histogram"),
        ui.tab(sepal_width_hist, title="Sepal Width Histogram"),
    )
    sepal_flex_tabs = ui.flex(sepal_text, sepal_tabs, direction="column")
    return ui.panel(sepal_flex_tabs, title="Sepal Panel")
@ui.component
def create_species_dashboard():
    species, set_species = ui.use_state()
    species_picker = ui.picker(
        species_table,
        on_change=set_species,
        selected_key=species,
        label="Current Species",
    )
    heatmap = ui.illustrated_message(
        ui.icon("vsFilter"),
        ui.heading("Species required"),
        ui.content("Select a species to display filtered table and chart."),
        width="100%",
    )
    if species:
        filtered_table = iris.where("Species = species")
        heatmap = dx.density_heatmap(filtered_table, x="SepalLength", y="SepalWidth")
    species_panel = ui.panel(
        ui.flex(species_picker, heatmap, direction="column"),
        title="Investigate Species",
    )
    sepal_panel = create_sepal_panel(set_species)
    return ui.column(
        ui.row(about_panel, iris_agg_stack, height=1),
        ui.row(sepal_panel, species_panel, height=2),
    )
iris_species_dashboard_state = ui.dashboard(create_species_dashboard())

ui.use_row_data and ui.badge
You’ve got a density heatmap that shows the density of SepalLength and SepalWidth by Species, but it’s tricky to see the min, max, and average overall values for SepalLength and SepalWidth for the selected Species. They’re in your table stack, but you can make them more prominent.
deephaven.ui provides hooks to access table data. Each hook provides access to a specific part of the table and is updated when the table changes.
The hooks are:
ui.use_cell_data- Accesses a single cell value.ui.use_row_data- Accesses a row of data.ui.use_row_list- Accesses a row as a list.ui.use_column_data- Accesses a column of data.ui.use_table_data- Accesses the entire table.
Warning
Filter your table to only the data you need the hook to pull out. The hooks do not filter the table for you.
Create a custom component that pulls the min, max, and average values for SepalLength and SepalWidth for the selected Species.
To display the values, wrap them in ui.badge components, which draw attention to specific values.
@ui.component
def summary_badges(species):
    # Filter the tables to the selected species
    species_min = iris_min.where("Species=species")
    species_max = iris_max.where("Species=species")
    species_avg = iris_avg.where("Species=species")
    # Pull the desired columns from the tables before using the hooks
    sepal_length_min = ui.use_cell_data(species_min.view(["SepalLength"]))
    sepal_width_min = ui.use_cell_data(species_min.view(["SepalWidth"]))
    sepal_length_max = ui.use_cell_data(species_max.view(["SepalLength"]))
    sepal_width_max = ui.use_cell_data(species_max.view(["SepalWidth"]))
    sepal_length_avg = ui.use_cell_data(species_avg.view(["SepalLength"]))
    sepal_width_avg = ui.use_cell_data(species_avg.view(["SepalWidth"]))
    # format the values to 3 decimal places
    # set flex_grow to 0 to prevent the badges from growing
    return ui.flex(
        ui.badge(f"SepalLength Min: {sepal_length_min:.3f}", variant="info"),
        ui.badge(f"SepalLength Max: {sepal_length_max:.3f}", variant="info"),
        ui.badge(f"SepalLength Avg: {sepal_length_avg:.3f}", variant="info"),
        ui.badge(f"SepalWidth Min: {sepal_width_min:.3f}", variant="info"),
        ui.badge(f"SepalWidth Max: {sepal_width_max:.3f}", variant="info"),
        ui.badge(f"SepalWidth Avg: {sepal_width_avg:.3f}", variant="info"),
        flex_grow=0,
    )
@ui.component
def create_species_dashboard():
    species, set_species = ui.use_state()
    species_picker = ui.picker(
        species_table,
        on_change=set_species,
        selected_key=species,
        label="Current Species",
    )
    heatmap = ui.illustrated_message(
        ui.icon("vsFilter"),
        ui.heading("Species required"),
        ui.content("Select a species to display filtered table and chart."),
        width="100%",
    )
    badges = None
    if species:
        filtered_table = iris.where("Species = species")
        heatmap = dx.density_heatmap(filtered_table, x="SepalLength", y="SepalWidth")
        badges = summary_badges(species)
    species_panel = ui.panel(
        ui.flex(species_picker, badges, heatmap, direction="column"),
        title="Investigate Species",
    )
    sepal_panel = create_sepal_panel(set_species)
    return ui.column(
        ui.row(about_panel, iris_agg_stack, height=1),
        ui.row(sepal_panel, species_panel, height=2),
    )
iris_species_dashboard_badge = ui.dashboard(create_species_dashboard())

ui.use_memo
ui.use_memo allows you to cache expensive calculations so they are only recalculated when needed. It takes a function and a list of dependencies. If the dependencies change, the function is recalculated.
Since you’ve added badges to the dashboard, the dx.heatmap is recreated every time any of the badges change, but only needs to be recreated when the Species changes.
Heatmap is a fairly expensive chart to create (it requires a filtered table in this case) and changes rarely, so pull the heatmap creation into a separate function and use ui.use_memo to cache the heatmap creation.
def create_heatmap(species):
    heatmap = ui.illustrated_message(
        ui.icon("vsFilter"),
        ui.heading("Species required"),
        ui.content("Select a species to display filtered table and chart."),
        width="100%",
    )
    if species:
        filtered_table = iris.where("Species = species")
        heatmap = dx.density_heatmap(filtered_table, x="SepalLength", y="SepalWidth")
    return heatmap
@ui.component
def create_species_dashboard():
    species, set_species = ui.use_state()
    species_picker = ui.picker(
        species_table,
        on_change=set_species,
        selected_key=species,
        label="Current Species",
    )
    heatmap = ui.use_memo(lambda: create_heatmap(species), [species])
    badges = summary_badges(species) if species else None
    species_panel = ui.panel(
        ui.flex(species_picker, badges, heatmap, direction="column"),
        title="Investigate Species",
    )
    sepal_panel = create_sepal_panel(set_species)
    return ui.column(
        ui.row(about_panel, iris_agg_stack, height=1),
        ui.row(sepal_panel, species_panel, height=2),
    )
iris_species_dashboard_final = ui.dashboard(create_species_dashboard())

Expand for final code
from deephaven import ui
import deephaven.plot.express as dx
from deephaven import agg
iris = dx.data.iris()
ui_iris = ui.table(
    iris,
    reverse=True,
    front_columns=["Timestamp", "Species"],
    hidden_columns=["PetalLength", "PetalWidth", "SpeciesID"],
    density="compact",
)
scatter_by_species = dx.scatter(iris, x="SepalLength", y="SepalWidth", by="Species")
sepal_text = ui.text("SepalLength vs. SepalWidth By Species Panel")
sepal_flex = ui.flex(ui_iris, scatter_by_species)
sepal_flex_column = ui.flex(sepal_text, sepal_flex, direction="column")
sepal_length_hist = dx.histogram(iris, x="SepalLength", by="Species")
sepal_width_hist = dx.histogram(iris, x="SepalWidth", by="Species")
sepal_tabs = ui.tabs(
    ui.tab(sepal_flex, title="Sepal Length vs. Sepal Width"),
    ui.tab(sepal_length_hist, title="Sepal Length Histogram"),
    ui.tab(sepal_width_hist, title="Sepal Width Histogram"),
)
sepal_flex_tabs = ui.flex(sepal_text, sepal_tabs, direction="column")
about_markdown = ui.markdown(r"""
### Iris Dashboard
Explore the Iris dataset with **deephaven.ui**
- The data powering this dashboard is simulated Iris data
- Charts are from Deephaven Plotly Express
- Other components are from **deephaven.ui**
  """)
sepal_panel = ui.panel(sepal_flex_tabs, title="Sepal Panel")
about_panel = ui.panel(about_markdown, title="About")
iris_avg = iris.agg_by([agg.avg(cols=["SepalLength", "SepalWidth", "PetalLength", "PetalWidth"])], by=["Species"])
iris_max = iris.agg_by([agg.max_(cols=["SepalLength", "SepalWidth", "PetalLength", "PetalWidth"])], by=["Species"])
iris_min = iris.agg_by([agg.min_(cols=["SepalLength", "SepalWidth", "PetalLength", "PetalWidth"])], by=["Species"])
ui_iris_avg = ui.panel(iris_avg, title="Average")
ui_iris_max = ui.panel(iris_max, title="Max")
ui_iris_min = ui.panel(iris_min, title="Min")
iris_agg_stack = ui.stack(ui_iris_avg, ui_iris_max, ui_iris_min)
species_table = iris.view("Species").select_distinct()
def create_sepal_panel(set_species):
  ui_iris = ui.table(
      iris,
      reverse=True,
      front_columns=["Timestamp", "Species"],
      hidden_columns=["PetalLength", "PetalWidth", "SpeciesID"],
      density="compact",
      on_row_double_press=lambda event: set_species(event["Species"]["value"])
    )
  sepal_flex = ui.flex(ui_iris, scatter_by_species)
  sepal_tabs = ui.tabs(
      ui.tab(sepal_flex, title="Sepal Length vs. Sepal Width"),
      ui.tab(sepal_length_hist, title="Sepal Length Histogram"),
      ui.tab(sepal_width_hist, title="Sepal Width Histogram"),
  )
  sepal_flex_tabs = ui.flex(sepal_text, sepal_tabs, direction="column")
  
  return ui.panel(sepal_flex_tabs, title="Sepal Panel")
  
@ui.component
def summary_badges(species):
  # Filter the tables to the selected species
  species_min = iris_min.where("Species=species")
  species_max = iris_max.where("Species=species")
  species_avg = iris_avg.where("Species=species")
  
  # Pull the desired columns from the tables before using the hooks
  sepal_length_min = ui.use_cell_data(species_min.view(["SepalLength"]))
  sepal_width_min = ui.use_cell_data(species_min.view(["SepalWidth"]))
  sepal_length_max = ui.use_cell_data(species_max.view(["SepalLength"]))
  sepal_width_max = ui.use_cell_data(species_max.view(["SepalWidth"]))
  sepal_length_avg = ui.use_cell_data(species_avg.view(["SepalLength"]))
  sepal_width_avg = ui.use_cell_data(species_avg.view(["SepalWidth"]))
  # format the values to 3 decimal places
  # set flex_grow to 0 to prevent the badges from growing 
  return ui.flex(
    ui.badge(f"SepalLength Min: {sepal_length_min:.3f}", variant="info"), 
    ui.badge(f"SepalLength Max: {sepal_length_max:.3f}", variant="info"),
    ui.badge(f"SepalLength Avg: {sepal_length_avg:.3f}", variant="info"),
    ui.badge(f"SepalWidth Min: {sepal_width_min:.3f}", variant="info"), 
    ui.badge(f"SepalWidth Max: {sepal_width_max:.3f}", variant="info"),
    ui.badge(f"SepalWidth Avg: {sepal_width_avg:.3f}", variant="info"),
    flex_grow=0
  )
def create_heatmap(species):
    heatmap = ui.illustrated_message(
        ui.icon("vsFilter"),
        ui.heading("Species required"),
        ui.content("Select a species to display filtered table and chart."),
        width="100%",
    )
  
    if species:
        filtered_table = iris.where("Species = species")
        heatmap = dx.density_heatmap(filtered_table, x="SepalLength", y="SepalWidth")
      
    return heatmap
  
@ui.component
def create_species_dashboard():
    species, set_species = ui.use_state()
    species_picker = ui.picker(
        species_table,
        on_change=set_species,
        selected_key=species,
        label="Current Species",
    )
  
    heatmap = ui.use_memo(lambda: create_heatmap(species), [species])
    badges = summary_badges(species) if species else None
    species_panel = ui.panel(
        ui.flex(species_picker, badges, heatmap, direction="column"),
        title="Investigate Species",
    )
    sepal_panel = create_sepal_panel(set_species)
    return ui.column(
        ui.row(about_panel, iris_agg_stack, height=1),
        ui.row(sepal_panel, species_panel, height=2),
    )
iris_species_dashboard_final = ui.dashboard(create_species_dashboard())
You’ve now completed this dashboard crash course with your custom component and interactivity.
Wrapping up
This wraps up the deephaven.ui dashboard crash course. In this course, you learned about the following components and concepts and created a dashboard with many of them: