Skip to main content

Form

The root that makes a screen a native modal form.

Import​

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

Usage​

packs/BP/scripts/settings.screen.tsx
export default function Settings(): JSX.Element {
return (
<Form onSubmit={({ values }) => apply(values)}>
<Text>{'Settings'}</Text>
<Toggle name={'music'} defaultValue={true} />
<Slider name={'volume'} min={0} max={10} />
<Input name={'nickname'} />
<Form.Button type={'submit'}>{'Save'}</Form.Button>
</Form>
);
}

Form is a host, the way <Screen> and <Container> are. Its presence makes the renderer build one atomic ModalFormData instead of a screen of buttons.

One member: Form.Button​

Form.Button, with type submit or exit, is the modal's own submit and dismiss button, and the only member Form has. Every field is a top-level component imported by its own name, and which hosts it works on is its own capability, not something a namespace decides:

import { Button, Dropdown, Form, Input, Option, Select, Slider, Text, Toggle } from '@bedrock-core/ui';

<Toggle>, <Select> and <Option> work on all three hosts and draw differently on each. <Input>, <Slider> and <Dropdown> only work here, because the engine draws no text field, slider or popup anywhere else. See Hosts for the whole table.

Props​

PropTypeDefaultDescription
onSubmit(event: SubmitEvent) => void—Called once when the player submits. event.values holds every control's value keyed by its name
onCancel(event: UiEvent) => void—Called when the player dismisses the modal — the X, Esc, or an exit button
childrenJSX.Node—The controls, the decoration and the action buttons, in any order

Form takes no control props: it is a marker, not a panel. Put a Panel inside it for a background, padding or a direction.

Values arrive once​

The native modal is atomic: nothing reaches script while it is open, and every value comes back together on submit.

<Form onSubmit={({ values }) => {
values.music; // boolean
values.volume; // number
values.nickname; // string
}}>

FormValues is Record<string, ModalValue>, where ModalValue is string | number | boolean | undefined. That is why a control here has a name and no onChange — there is nothing to call one from.

A heading is a <Text>: the modal has no title or body prop of its own.

Rules​

The build enforces these, with a message naming the fix.

  • Exactly one submit. A modal has no built-in submit control, so the screen declares one: <Form.Button type={'submit'}>. At most one <Form.Button type={'exit'}> beside it.
  • No nested root. A <Form> inside a <Form>, or a <Screen> or <Container> inside one, is refused. Mix the two form kinds across separate screens, never nested.
  • Only what this host can draw. A <Slot> or a plain press with a handler is refused here by name — a modal draws its typed controls plus its own two actions and nothing else.

Notes​

A form cannot be mutated while it is open, so a state change never repaints it. The player sees a new snapshot when they press. State covers what follows from that.

Next steps​