# Which widget to use

> Every widget of xis-mvui at a glance - what the player sees, when to pick it, the options all widgets share and which may go in the footer.

URL: https://xichil.dev/docs/xis-mvui/v1/widgets/ · Updated: 2026-11-06 · Applies to: Fabric, Quilt, Legacy Fabric, Forge, NeoForge, MC 1.7.10 - 26.3

A screen is a [MenuScreen](https://xichil.dev/javadoc/xis-mvui/v1/dev/xichil/mvui/MenuScreen.html): a title, a scrolling list of entries (one
widget per row) and a footer row of buttons. The widget set is fixed; you compose screens from these.

## Pick a widget

| You need… | Use | The player sees |
| --- | --- | --- |
| an action | [Button](https://xichil.dev/docs/xis-mvui/v1/widgets/button/index.md) | a vanilla button |
| read-only text | [Label](https://xichil.dev/docs/xis-mvui/v1/widgets/label/index.md) | text on its own row; long text wraps |
| the result of an action | [StatusLine](https://xichil.dev/docs/xis-mvui/v1/widgets/status-line/index.md) | a colored message line, empty until shown |
| an on/off setting | [Toggle](https://xichil.dev/docs/xis-mvui/v1/widgets/toggle/index.md) | a button "label: ON" / "label: OFF" |
| several independent yes/no choices | [Checkbox](https://xichil.dev/docs/xis-mvui/v1/widgets/checkbox/index.md) | a check box with its label beside it |
| one of a few values (an enum) | [Cycle](https://xichil.dev/docs/xis-mvui/v1/widgets/cycle/index.md) | a button "label: value" that steps on each click |
| one of many values | [Dropdown](https://xichil.dev/docs/xis-mvui/v1/widgets/dropdown/index.md) | like a cycle; a click opens a picker screen |
| a list of items, one selected | [SelectList](https://xichil.dev/docs/xis-mvui/v1/widgets/select-list/index.md) | a scrolling list like the world list |
| a number in a range, dragged | [Slider](https://xichil.dev/docs/xis-mvui/v1/widgets/slider/index.md) | a vanilla slider "label: value" |
| an exact number, typed | [NumberField](https://xichil.dev/docs/xis-mvui/v1/widgets/number-field/index.md) | a text field with range errors below it |
| one line of text | [TextInput](https://xichil.dev/docs/xis-mvui/v1/widgets/text-input/index.md) | a text field with a label above and an error line below |
| several lines of text | [MultiLineInput](https://xichil.dev/docs/xis-mvui/v1/widgets/multi-line-input/index.md) | a text box of 2 – 12 lines with word wrap |
| a list of strings to edit | [StringListEditor](https://xichil.dev/docs/xis-mvui/v1/widgets/string-list-editor/index.md) | a list, an input field, Add and Remove |
| a key or mouse button | [Keybind](https://xichil.dev/docs/xis-mvui/v1/widgets/keybind/index.md) | a button "label: key" like the Controls screen |
| a color | [ColorPicker](https://xichil.dev/docs/xis-mvui/v1/widgets/color-picker/index.md) | a button "label: #RRGGBB" with a swatch; opens an editor |
| a group of entries | [Section](https://xichil.dev/docs/xis-mvui/v1/widgets/section/index.md) | a titled group, collapsible ("▶ title" / "▼ title") |

The other screen type is [ConfirmDialog](https://xichil.dev/javadoc/xis-mvui/v1/dev/xichil/mvui/ConfirmDialog.html), a yes/no question on its own
screen ([Several screens and dialogs](https://xichil.dev/docs/xis-mvui/v1/guides/navigation/index.md)).

## How every widget is built

Each widget has a builder that starts with an id and ends with `build()`:

```java
Toggle sounds = Toggle.builder("sounds")          // id: unique on the screen
        .label(Text.translatable("mymod.sounds", "Sounds"))
        .value(() -> config.sounds)
        .onChange(v -> config.sounds = v)
        .build();                                  // throws if a required field is missing
```

A missing required field makes `build()` throw `IllegalStateException` naming the widget and the field. Setters
of optional fields reject `null` at once.

## Options every widget has

| Builder method | Default | Effect |
| --- | --- | --- |
| `enabled(BooleanSupplier)` or `enabled(boolean)` | enabled | while false the widget is greyed out and ignores input; it still shows its label, value and tooltip |
| `tooltip(Supplier<Text>)` or `tooltip(Text)` | none | text shown while the mouse is over the widget; the supplier may return `null` for none. Disabled widgets show it too, so it's a good place to say why. |
| `visible(BooleanSupplier)` | visible | while false the entry has no row at all, and the rows below move up |

```java
Button.builder("export")
        .label(Text.translatable("mymod.export", "Export"))
        .onPress(this::export)
        .enabled(() -> !profiles.isEmpty())
        .tooltip(() -> profiles.isEmpty() ? Text.translatable("mymod.export.none", "Nothing to export") : null)
        .visible(() -> config.advanced)
        .build();
```

Every setter that takes a supplier also has a constant overload; the supplier form is re-read on every refresh
([State and refresh](https://xichil.dev/docs/xis-mvui/v1/concepts/state-and-refresh/index.md)).

## The footer

`MenuScreen.Builder.footer(entry)` puts an entry in the row at the bottom, which stays put while the list
scrolls. Use it for the screen's main actions: Done, Save, Cancel.

| May go in the footer | May not |
| --- | --- |
| Button, Label, StatusLine, Toggle, Cycle, Dropdown, SelectList, TextInput, NumberField | Checkbox, Slider, MultiLineInput, StringListEditor, Keybind, ColorPicker, Section |

A footer entry also must have no `visible(…)` and must not be in a section. Anything else makes `footer(…)` throw.
In practice, keep the footer to a few buttons.

## Building a screen

```java
MenuScreen.builder("main")
        .title(Text.translatable("mymod.title", "My Mod"))   // required
        .add(entry)                                         // a row; a Section brings its children along
        .add(stringListEditor)                              // its four entries
        .footer(button)                                     // the bottom row, left to right
        .build();                                           // throws on a missing title or a duplicate id
```
