gpui-component/docs/docs/components/form.md

16 KiB

title description
Form Flexible form container with support for field layout, validation, and multi-column layouts.

Form

A comprehensive form component that provides structured layout for form fields with support for vertical/horizontal layouts, validation, field groups, and responsive multi-column layouts.

Import

use gpui_component::form::{form_field, v_form, h_form, Form, FormField};

Usage

Basic Form

v_form()
    .child(
        form_field()
            .label("Name")
            .child(TextInput::new(&name_input))
    )
    .child(
        form_field()
            .label("Email")
            .child(TextInput::new(&email_input))
            .required(true)
    )

Horizontal Form Layout

h_form()
    .label_width(px(120.))
    .child(
        form_field()
            .label("First Name")
            .child(TextInput::new(&first_name))
    )
    .child(
        form_field()
            .label("Last Name")
            .child(TextInput::new(&last_name))
    )

Multi-Column Form

v_form()
    .column(2) // Two-column layout
    .child(
        form_field()
            .label("First Name")
            .child(TextInput::new(&first_name))
    )
    .child(
        form_field()
            .label("Last Name")
            .child(TextInput::new(&last_name))
    )
    .child(
        form_field()
            .label("Bio")
            .col_span(2) // Span across both columns
            .child(TextInput::new(&bio_input))
    )

Form Container and Layout

Vertical Layout (Default)

v_form()
    .gap(px(12.))
    .child(form_field().label("Name").child(input))
    .child(form_field().label("Email").child(email_input))

Horizontal Layout

h_form()
    .label_width(px(100.))
    .child(form_field().label("Name").child(input))
    .child(form_field().label("Email").child(email_input))

Custom Sizing

v_form()
    .large() // Large form size
    .label_text_size(rems(1.2))
    .child(form_field().label("Title").child(input))

v_form()
    .small() // Small form size
    .child(form_field().label("Code").child(input))

Form Validation

Required Fields

form_field()
    .label("Email")
    .required(true) // Shows asterisk (*) next to label
    .child(TextInput::new(&email_input))

Field Descriptions

form_field()
    .label("Password")
    .description("Must be at least 8 characters long")
    .child(TextInput::new(&password_input))

Dynamic Descriptions

form_field()
    .label("Bio")
    .description_fn(|_, _| {
        div().child("Use at most 100 words to describe yourself.")
    })
    .child(TextInput::new(&bio_input))

Field Visibility

form_field()
    .label("Admin Settings")
    .visible(user.is_admin()) // Conditionally show field
    .child(Switch::new("admin-mode"))

Submit Handling

Basic Submit Pattern

struct FormView {
    name_input: Entity<InputState>,
    email_input: Entity<InputState>,
}

impl FormView {
    fn submit(&mut self, cx: &mut Context<Self>) {
        let name = self.name_input.read(cx).value();
        let email = self.email_input.read(cx).value();

        // Validate inputs
        if name.is_empty() || email.is_empty() {
            // Show validation error
            return;
        }

        // Submit form data
        self.handle_submit(name, email, cx);
    }
}

// Form with submit button
v_form()
    .child(form_field().label("Name").child(TextInput::new(&self.name_input)))
    .child(form_field().label("Email").child(TextInput::new(&self.email_input)))
    .child(
        form_field()
            .no_label_indent()
            .child(
                Button::new("submit")
                    .primary()
                    .child("Submit")
                    .on_click(cx.listener(|this, _, _, cx| this.submit(cx)))
            )
    )

Form with Action Buttons

v_form()
    .child(form_field().label("Title").child(TextInput::new(&title)))
    .child(form_field().label("Content").child(TextInput::new(&content)))
    .child(
        form_field()
            .no_label_indent()
            .child(
                h_flex()
                    .gap_2()
                    .child(Button::new("save").primary().child("Save"))
                    .child(Button::new("cancel").child("Cancel"))
                    .child(Button::new("preview").outline().child("Preview"))
            )
    )

Field Groups

v_form()
    .child(
        form_field()
            .label("Name")
            .child(
                h_flex()
                    .gap_2()
                    .child(div().flex_1().child(TextInput::new(&first_name)))
                    .child(div().flex_1().child(TextInput::new(&last_name)))
            )
    )
    .child(
        form_field()
            .label("Address")
            .items_start() // Align to start for multi-line content
            .child(
                v_flex()
                    .gap_2()
                    .child(TextInput::new(&street))
                    .child(
                        h_flex()
                            .gap_2()
                            .child(div().flex_1().child(TextInput::new(&city)))
                            .child(div().w(px(100.)).child(TextInput::new(&zip)))
                    )
            )
    )

Custom Field Components

form_field()
    .label("Theme Color")
    .child(ColorPicker::new(&color_state).small())

form_field()
    .label("Birth Date")
    .description("We'll send you a birthday gift!")
    .child(DatePicker::new(&date_state))

form_field()
    .label("Notifications")
    .child(
        v_flex()
            .gap_2()
            .child(Switch::new("email").label("Email notifications"))
            .child(Switch::new("push").label("Push notifications"))
            .child(Switch::new("sms").label("SMS notifications"))
    )

Conditional Fields

v_form()
    .child(
        form_field()
            .label("Account Type")
            .child(Dropdown::new(&account_type))
    )
    .child(
        form_field()
            .label("Company Name")
            .visible(is_business_account) // Show only for business accounts
            .child(TextInput::new(&company_name))
    )
    .child(
        form_field()
            .label("Tax ID")
            .visible(is_business_account)
            .required(is_business_account)
            .child(TextInput::new(&tax_id))
    )

Grid Layout and Positioning

Column Spanning

v_form()
    .column(3) // Three-column grid
    .child(form_field().label("First").child(input1))
    .child(form_field().label("Second").child(input2))
    .child(form_field().label("Third").child(input3))
    .child(
        form_field()
            .label("Full Width")
            .col_span(3) // Spans all three columns
            .child(TextInput::new(&full_width))
    )

Column Positioning

v_form()
    .column(4)
    .child(form_field().label("A").child(input_a))
    .child(form_field().label("B").child(input_b))
    .child(
        form_field()
            .label("Positioned")
            .col_start(1) // Start at column 1
            .col_span(2)  // Span 2 columns
            .child(input_positioned)
    )

Responsive Layout

v_form()
    .column(if is_mobile { 1 } else { 2 })
    .child(form_field().label("Name").child(name_input))
    .child(form_field().label("Email").child(email_input))
    .child(
        form_field()
            .label("Bio")
            .when(!is_mobile, |field| field.col_span(2))
            .child(bio_input)
    )

API Reference

Form

Method Description
vertical() Create vertical layout form
horizontal() Create horizontal layout form
layout(Axis) Set form layout direction
label_width(Pixels) Set label width (horizontal layout)
label_text_size(Rems) Set label text size
gap(Pixels) Set gap between fields
column(u16) Set number of columns for grid layout
child(FormField) Add form field
children(iter) Add multiple form fields
large() Set large size
small() Set small size

FormField

Method Description
label(str) Set field label
label_fn(fn) Set dynamic label using function
description(str) Set field description
description_fn(fn) Set dynamic description
required(bool) Mark field as required
visible(bool) Set field visibility
no_label_indent() Disable label indentation
items_start() Align items to start
items_center() Align items to center
items_end() Align items to end
col_span(u16) Set column span
col_start(i16) Set starting column
col_end(i16) Set ending column
track_focus(handle) Track focus for field

Examples

User Registration Form

struct RegistrationForm {
    first_name: Entity<InputState>,
    last_name: Entity<InputState>,
    email: Entity<InputState>,
    password: Entity<InputState>,
    confirm_password: Entity<InputState>,
    terms_accepted: bool,
}

impl Render for RegistrationForm {
    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
        v_form()
            .large()
            .child(
                form_field()
                    .label("Personal Information")
                    .no_label_indent()
                    .child(
                        h_flex()
                            .gap_3()
                            .child(
                                div().flex_1().child(
                                    TextInput::new(&self.first_name)
                                        .placeholder("First name")
                                )
                            )
                            .child(
                                div().flex_1().child(
                                    TextInput::new(&self.last_name)
                                        .placeholder("Last name")
                                )
                            )
                    )
            )
            .child(
                form_field()
                    .label("Email")
                    .required(true)
                    .child(TextInput::new(&self.email))
            )
            .child(
                form_field()
                    .label("Password")
                    .required(true)
                    .description("Must be at least 8 characters")
                    .child(TextInput::new(&self.password))
            )
            .child(
                form_field()
                    .label("Confirm Password")
                    .required(true)
                    .child(TextInput::new(&self.confirm_password))
            )
            .child(
                form_field()
                    .no_label_indent()
                    .child(
                        Checkbox::new("terms")
                            .label("I agree to the Terms of Service")
                            .checked(self.terms_accepted)
                            .on_click(cx.listener(|this, checked, _, cx| {
                                this.terms_accepted = *checked;
                                cx.notify();
                            }))
                    )
            )
            .child(
                form_field()
                    .no_label_indent()
                    .child(
                        Button::new("register")
                            .primary()
                            .large()
                            .w_full()
                            .child("Create Account")
                    )
            )
    }
}

Settings Form with Sections

v_form()
    .column(2)
    .child(
        form_field()
            .label("Profile")
            .no_label_indent()
            .col_span(2)
            .child(Divider::horizontal())
    )
    .child(
        form_field()
            .label("Display Name")
            .child(TextInput::new(&display_name))
    )
    .child(
        form_field()
            .label("Email")
            .child(TextInput::new(&email))
    )
    .child(
        form_field()
            .label("Bio")
            .col_span(2)
            .items_start()
            .child(TextInput::new(&bio))
    )
    .child(
        form_field()
            .label("Preferences")
            .no_label_indent()
            .col_span(2)
            .child(Divider::horizontal())
    )
    .child(
        form_field()
            .label("Theme")
            .child(Dropdown::new(&theme_state))
    )
    .child(
        form_field()
            .label("Language")
            .child(Dropdown::new(&language_state))
    )
    .child(
        form_field()
            .no_label_indent()
            .child(Switch::new("notifications").label("Enable notifications"))
    )
    .child(
        form_field()
            .no_label_indent()
            .child(Switch::new("marketing").label("Marketing emails"))
    )

Contact Form

v_form()
    .child(
        form_field()
            .label("Contact Information")
            .child(
                h_flex()
                    .gap_2()
                    .child(
                        Dropdown::new(&prefix_state)
                            .w(px(80.))
                    )
                    .child(
                        div().flex_1().child(
                            TextInput::new(&name_input)
                                .placeholder("Your name")
                        )
                    )
            )
    )
    .child(
        form_field()
            .label("Email")
            .required(true)
            .child(TextInput::new(&email_input))
    )
    .child(
        form_field()
            .label("Subject")
            .child(Dropdown::new(&subject_state))
    )
    .child(
        form_field()
            .label("Message")
            .required(true)
            .items_start()
            .description("Please describe your inquiry in detail")
            .child(TextInput::new(&message_input))
    )
    .child(
        form_field()
            .no_label_indent()
            .child(
                h_flex()
                    .gap_2()
                    .justify_between()
                    .child(
                        Checkbox::new("copy")
                            .label("Send me a copy")
                    )
                    .child(
                        h_flex()
                            .gap_2()
                            .child(Button::new("cancel").child("Cancel"))
                            .child(Button::new("send").primary().child("Send Message"))
                    )
            )
    )

Accessibility

  • Keyboard Navigation: Full keyboard support for all form elements
  • Focus Management: Proper focus indicators and tab order
  • Screen Readers: Labels properly associated with form controls
  • Required Fields: Visual and semantic indication of required fields
  • Error States: Clear error messaging and validation feedback
  • ARIA Labels: Proper ARIA attributes for complex form structures
  • Semantic HTML: Uses appropriate form semantics for accessibility tools