Skip to main content

Hosts

A host is one Minecraft screen the library draws on, plus the transport that screen offers. The root element of a screen names its host, and there is no default.

RootHostOwnerServed byWhat an interaction is
<Screen>form-actionthe playerrender(Screen, player)a form entry the engine reports back by index
<Form>form-modalthe playerrender(Form, player)a native field the engine owns, returned in one answer on submit
<Container entity|block>chestthe entity or blockcreateContainerScreen(Screen)an item moving through a slot of the host's own container

All three lay out against the same 320 × 210 canvas. A tree that starts with anything else throws ScreenRootError listing the roots.

What each host can carry​

A host declares what every kind of component becomes on it. A kind the table does not name has nothing to be there, so it is refused at build with that host's own wording rather than drawn inert.

Componentform-actionform-modalchest
Buttona pressits 'submit' and 'exit' onlyan item taken and put back
Togglea press that flips and re-presentsa native fielda cell of the host's container
Select, Optiona press per optiona native fielda cell per option
Slider, Dropdown, Input❌a native field❌
Slot❌❌a cell the player fills
SlotGrid❌❌a collection the engine already publishes

Components absent from the table — Panel, Text, Image, Background, Fragment, Scroll — draw on every host and ask for nothing.

A root has no members​

This table is why fields are top-level components rather than members of Form. A root names a host, and Form.Button is its one member: every field is a top-level component, and the row above is what decides where it can be written.

So <Toggle> is one component with three mechanisms — on <Screen> a press that flips its state and renders the screen again, on <Form> a field the engine owns until submit, on <Container> a cell whose press is an item taken and put straight back. The same source moves between all three:

import { Toggle } from '@bedrock-core/ui';

<Toggle name={'music'} defaultValue={true} on={music} onChange={setMusic} />

Each host uses the props it can — name is what a modal reports under, on and onChange are what the two press hosts give back — and ignores the rest.

<Input>, <Slider> and <Dropdown> are the other shape: one host each, because the engine draws no text field, slider or popup outside a modal. Writing one elsewhere is a build error naming the fix, not a control drawn inert.

Live values travel on carriers: bool, int, enum and text on both form hosts, and bool, int and text on the chest. A container publishes no text of its own, so a string crosses one character per slot.

One component set​

useMechanism is the seam: a component asks what its kind becomes here and draws that, so a toggle is a native field on a modal and a pressed button on a screen of buttons without knowing which screen it is on. What differs between hosts is the mechanism, never the component.

That is also what makes the refusals precise. The same <Slider> is "put it inside a <Form>" on an action form and "a container has no native form" on a container screen, because the fix is different and the host is the one that knows it.

Writing a fragment for a host you do not own​

A root names the host, so anything under it already knows what it becomes. <Expect host> is for the other case: a component library that renders into a screen it does not own — a set of fields meant for a modal, exported as a fragment for an addon to place.

import { Expect, Form } from '@bedrock-core/ui';

export const AccountFields = (): JSX.Element => (
<Expect host={'form-modal'}>
<Input name={'nickname'} />
<Toggle name={'notify'} />
</Expect>
);

An addon that drops that fragment on the wrong screen is told where, once, instead of being told about each field in turn.

Forms cannot change while open​

Both form hosts are serialized for one player when the screen is shown, and @minecraft/server-ui forms cannot be mutated while open. So a state change never repaints a form on its own: the component logic keeps running, and the player sees a new snapshot when they press.

A container screen has no such limit. A handler renders and the slots settle in the same tick, because everything alive in it travels through the host's container rather than through a form payload.

Next steps​

  • Navigation — opening another screen by key, and what a press can be described as
  • State — where a screen's hook state lives, and how long it lasts
  • Container screens — serving a screen an entity or a block owns
  • Compiler — how the screen in the pack was produced