SegmentedControl

SegmentedControl is the compact, always-visible alternative to a dropdown for a small set of mutually exclusive choices.
It works well when the user should be able to compare the available modes at a glance, such as switching between tabs, filters, or display modes. It is not a good fit for large or dynamic option sets.

Example

use fission::prelude::*;
use std::sync::Arc;

let widget: Widget = SegmentedControl {
options: vec!["All".into(), "Unread".into(), "Flagged".into()],
selected_index: view.state().filter_mode,
on_change: Some(Arc::new(move |index| ActionEnvelope {
    id: set_filter_mode_id,
    payload: serde_json::to_vec(&SetFilterMode(index)).unwrap(),
})),
}
.into();
Each segment is just a button under the hood. The selected segment is derived from state on every build.

Field table

Field
Type
Meaning
Notes / default behavior
options
Vec<String>
Labels for each segment.
Required. Keep the set short.
selected_index
usize
Which option is currently active.
Controlled by app state.
on_change
Option<Arc<dyn Fn(usize) -> ActionEnvelope + Send + Sync>>
Closure that creates the action for a newly chosen index.
Called once per segment during build to prepare that segment's action.

How it fits the state loop

The closure returns an ActionEnvelope for the chosen index. When the user presses a segment, that action is dispatched, the reducer updates the selected index in GlobalState, and the next build re-renders the control with the new active segment.
Because the closure runs during component conversion to prepare per-segment actions, keep it cheap and deterministic. It should package data, not do work.

Specific advice

Use segmented controls for short, peer-level choices. If labels start wrapping, if there are more than a handful of options, or if options may disappear behind scrolling, you probably want Select or Radio instead.

Production checklist

For SegmentedControl, review the fields that change behavior before treating the widget as finished: options, selected_index, on_change. The goal is to make the product rule visible in state and actions, not hidden inside ad-hoc construction code.
Bind on_change to explicit reducer actions and test that the reducer handles unavailable, duplicate, or invalid input safely.
Check the semantics tree for the user-facing label or role that makes this widget understandable without relying only on pixels.
Add at least one component or harness test that confirms the visible text, semantic role, action dispatch, and layout constraint that matter for this widget in context.
If a screen starts repeating the same SegmentedControl setup, extract a named component around this widget. That keeps the reference API small while making product code easier to read and safer for generated code to copy.
Radio, Select, Button, and Tabs.
Fission
A cross-platform, GPU-accelerated user interface framework for Rust. MIT licensed.
Copyright (c) 2026 Fission
Ready to use today. Widget APIs are expected to remain stable; some runtime and shell APIs may change before 1.0.0.
Fission 0.7.0