> Markdown version of [Form Layout](https://vaadin.com/docs/v23/components/form-layout). Section index: [llms.txt](https://vaadin.com/docs/v23/components/llms.txt)

# Form Layout

Form Layout allows you to build responsive forms with multiple columns, and to position input labels above or to the side of the input.

**TypeScript** — `form-layout-basic.ts`

```typescript
private responsiveSteps: FormLayoutResponsiveStep[] = [
  // Use one column by default
  { minWidth: 0, columns: 1 },
  // Use two columns, if layout's width exceeds 500px
  { minWidth: '500px', columns: 2 },
];

protected override render() {
  return html`
    <vaadin-form-layout .responsiveSteps="${this.responsiveSteps}">
      <vaadin-text-field label="First name"></vaadin-text-field>
      <vaadin-text-field label="Last name"></vaadin-text-field>
      <!-- Stretch the username field over 2 columns -->
      <vaadin-text-field colspan="2" label="Username"></vaadin-text-field>
      <vaadin-password-field label="Password"></vaadin-password-field>
      <vaadin-password-field label="Confirm password"></vaadin-password-field>
    </vaadin-form-layout>
  `;
}
```

**Java** — `FormLayoutBasic.java`

```java
TextField firstName = new TextField("First name");
TextField lastName = new TextField("Last name");
TextField username = new TextField("Username");
PasswordField password = new PasswordField("Password");
PasswordField confirmPassword = new PasswordField("Confirm password");

FormLayout formLayout = new FormLayout();
formLayout.add(firstName, lastName, username, password,
        confirmPassword);
formLayout.setResponsiveSteps(
        // Use one column by default
        new ResponsiveStep("0", 1),
        // Use two columns, if layout's width exceeds 500px
        new ResponsiveStep("500px", 2));
// Stretch the username field over 2 columns
formLayout.setColspan(username, 2);
```

## <a id="columns"></a>Columns

By default, Form Layout has two columns, meaning it displays two input fields per line. When the layout width is smaller, it adjusts to a single-column.

### <a id="custom-layout"></a>Custom Layout

You can define how many columns Form Layout should use based on the screen width.

> **Important:** Use the draggable split handle to resize Form Layout’s available space and to test its responsiveness.

**TypeScript** — `form-layout-custom-layout.ts`

```typescript
private responsiveSteps: FormLayoutResponsiveStep[] = [
  // Use one column by default
  { minWidth: 0, columns: 1 },
  // Use two columns, if the layout's width exceeds 320px
  { minWidth: '320px', columns: 2 },
  // Use three columns, if the layout's width exceeds 500px
  { minWidth: '500px', columns: 3 },
];

protected override render() {
  return html`
    <vaadin-split-layout>
      <vaadin-form-layout .responsiveSteps="${this.responsiveSteps}">
        <vaadin-text-field label="First name"></vaadin-text-field>
        <vaadin-text-field label="Last name"></vaadin-text-field>
        <vaadin-email-field label="Email"></vaadin-email-field>
      </vaadin-form-layout>
      <div></div>
    </vaadin-split-layout>
  `;
}
```

**Java** — `FormLayoutCustomLayout.java`

```java
formLayout.setResponsiveSteps(
        // Use one column by default
        new ResponsiveStep("0", 1),
        // Use two columns, if the layout's width exceeds 320px
        new ResponsiveStep("320px", 2),
        // Use three columns, if the layout's width exceeds 500px
        new ResponsiveStep("500px", 3));
```

A single-column layout is preferable to a multi-column one. A multi-column layout can be prone to confusion and misinterpretation by the user.

However, related fields can be placed in a line without confusion, typically. Examples of this would be first and last name, address fields such as postal code and city, and ranged input for dates, time, and currency.

### <a id="column-span"></a>Column Span

When using a multi-column layout, you can define a `colspan` for each component. The `colspan` determines how many columns a component extends or stretches across.

For example, if you have a Form Layout with three columns and a component’s `colspan` is set to 3, it takes up the entire width of the Form Layout.

**TypeScript** — `form-layout-colspan.ts`

```html
<vaadin-text-field label="Title" colspan="3"></vaadin-text-field>
```

**Java** — `FormLayoutColspan.java`

```java
formLayout.setColspan(title, 3);
```

## <a id="label-position"></a>Label Position

Input fields' built-in labels are positioned above the input. Form Layout supports side-positioned labels, provided they are wrapped in Form Items and the label position is set to `aside`.

The only reason for wrapping labels in Form Items is to put the labels to the side of the input.

### <a id="top"></a>Top

Users complete forms that have top-positioned labels more quickly because they provide a consistent scanning pattern — top-down, as opposed to zigzag — while minimizing the distance between the label and input.

Top-positioned labels are also less prone to causing layout issues due to variable label lengths, which happens usually in multilingual applications. However, they do result in vertically longer forms. This is why [sectioning](#sectioning) is important.

### <a id="side"></a>Side

Side-positioned labels help reduce a form’s total height. This is especially useful for longer forms and when vertical space is limited.

Labels positioned on the side are also often used when there is a need to compare numeric data.

**TypeScript** — `form-layout-side.ts`

```html
<vaadin-form-layout>
  <!-- Wrap fields into form items, which
       displays labels on the side by default -->
  <vaadin-form-item>
    <label slot="label">Revenue</label>
    <vaadin-text-field>
      <span slot="suffix">EUR</span>
    </vaadin-text-field>
  </vaadin-form-item>
  <vaadin-form-item>
    <label slot="label">Expenses</label>
    <vaadin-text-field>
      <span slot="suffix">EUR</span>
    </vaadin-text-field>
  </vaadin-form-item>
  <vaadin-form-item>
    <label slot="label">Invoices</label>
    <vaadin-text-field>
      <span slot="suffix">EUR</span>
    </vaadin-text-field>
  </vaadin-form-item>
</vaadin-form-layout>
```

**Java** — `FormLayoutSide.java`

```java
// Use addFormItem instead of add, to wrap fields into form items,
// which displays labels on the side by default
formLayout.addFormItem(revenue, "Revenue");
formLayout.addFormItem(expenses, "Expenses");
formLayout.addFormItem(invoices, "Invoices");
```

Aim for similar-length labels to keep the distance between the labels and input fields consistent. Inconsistent spacing can slow the user in completing the form.

Forms that use this position require more horizontal space, which isn’t always ideal in narrow forms. It’s recommended to configure Form Layout to use top-positioned labels when the form has a narrow width.

### <a id="responsive-label-position"></a>Responsive Label Position

Similar to the number of columns, the label position is configurable based on the width of the layout. For example, you can position the labels to the side when there is ample horizontal space available, and on top for narrower screens.

## <a id="native-input-fields"></a>Native Input Fields

Form Item allows you to set a label for any type of component that you want to use in a Form Layout. It supports both Vaadin components and native HTML components.

**TypeScript** — `form-layout-native-input.ts`

```html
<vaadin-form-layout>
  <vaadin-form-item>
    <label slot="label">Revenue</label>
    <input type="text" />
  </vaadin-form-item>
</vaadin-form-layout>
```

**Java** — `FormLayoutNativeInput.java`

```java
formLayout.addFormItem(new Input(), "Revenue");
```

## <a id="multiple-fields"></a>Multiple Fields

Form Item only supports placing a single field inside. Where you need to place multiple fields, [Custom Field](https://vaadin.com/docs/v23/components/custom-field.md) should be used as a wrapper:

**TypeScript** — `form-layout-custom-field.ts`

```js
@query('#month-field')
private monthField!: Select;

@query('#year-field')
private yearField!: Select;

@state()
private months = Array.from({ length: 12 }, (_, i) => `${i + 1}`.padStart(2, '0'));

@state()
private years = Array.from({ length: 11 }, (_, i) => `${i + new Date().getFullYear()}`);

protected override firstUpdated() {
  // Set title for screen readers
  this.monthField.focusElement!.setAttribute('title', 'Month');
  this.yearField.focusElement!.setAttribute('title', 'Year');
}

render() {
  return html`
    <vaadin-form-layout>
      <vaadin-form-item>
        <label slot="label">Expiration</label>
        <vaadin-custom-field
          .parseValue="${(value: string) => {
            return value ? value.split('/') : ['', ''];
          }}"
          .formatValue="${(values: unknown[]) => {
            return values[0] && values[1] ? values.join('/') : '';
          }}"
        >
          <vaadin-horizontal-layout theme="spacing-xs">
            <vaadin-select
              id="month-field"
              placeholder="Month"
              .items="${this.months.map((month) => ({ label: month, value: month }))}"
            ></vaadin-select>
            <vaadin-select
              id="year-field"
              placeholder="Year"
              .items="${this.years.map((year) => ({ label: year, value: year }))}"
            ></vaadin-select>
          </vaadin-horizontal-layout>
        </vaadin-custom-field>
      </vaadin-form-item>
    </vaadin-form-layout>
  `;
}
```

**Java** — `FormLayoutCustomField.java`

```java
public class ExpirationField extends CustomField<String> {
    private final List<String> MONTHS = IntStream.range(1, 13)
            .mapToObj(month -> String.format("%02d", month))
            .collect(Collectors.toList());
    private final List<String> YEARS = IntStream
            .range(LocalDate.now().getYear(),
                    LocalDate.now().getYear() + 11)
            .mapToObj(year -> Integer.toString(year))
            .collect(Collectors.toList());

    private final Select<String> month = new Select<String>();
    private final Select<String> year = new Select<String>();

    public ExpirationField() {
        HorizontalLayout layout = new HorizontalLayout();
        layout.setPadding(false);
        layout.setSpacing(false);
        layout.getThemeList().add("spacing-xs");

        month.setItems(MONTHS);
        month.setPlaceholder("Month");
        // Set title for screen readers
        month.getElement().executeJs(
                "this.focusElement.setAttribute('title', 'Month');");
        layout.add(month);

        year.setItems(YEARS);
        year.setPlaceholder("Year");
        year.getElement().executeJs(
                "this.focusElement.setAttribute('title', 'Year');");
        layout.add(year);

        add(layout);
    }

    @Override
    protected String generateModelValue() {
        String monthValue = month.getValue();
        String yearValue = year.getValue();

        if (monthValue == month.getEmptyValue()
                || yearValue == year.getEmptyValue()) {
            return null;
        }

        return String.join("/", monthValue.toString(),
                yearValue.toString());
    }

    @Override
    protected void setPresentationValue(String expiration) {
        if (expiration == null) {
            month.setValue(null);
            year.setValue(null);
        } else {
            String[] expirationParts = expiration.split("/");
            month.setValue(expirationParts[0]);
            year.setValue(expirationParts[1]);
        }

    }
}

public FormLayoutCustomField() {
    FormLayout formLayout = new FormLayout();
    formLayout.addFormItem(new ExpirationField(), "Expiration");
    add(formLayout);
}
```

Keeping fields in individual Form Items, however, is preferable. Wrapped fields can be hard to distinguish visually since they usually have no individual label except for a placeholder, which is only visible when the field has no value.

## <a id="best-practices"></a>Best Practices

### <a id="sectioning"></a>Sectioning

Longer forms should be split into smaller, more manageable and user-friendly sections using sub-headings, [Tabs](https://vaadin.com/docs/v23/components/tabs.md), [Details](https://vaadin.com/docs/v23/components/details.md) or separate views when possible. Each section should consist of related content and fields.

### <a id="button-placement"></a>Button Placement

Use the following guidelines for Button placement in forms:

- Buttons should be placed below the form with which they are associated.

- Buttons should be aligned to the left.

- Primary action should be placed first, followed by other actions, in order of importance.

For more information, see the [Button documentation](https://vaadin.com/docs/v23/components/button.md).

`7B8E4F98-540C-4622-A39F-907C95E9DFFD`
