Airframe GitHub Storybook
Component

ba-form

ba-form is used to handle form interactions when using our other form elements

Airframe

Active
Figma

Web Components

Active
Storybook GitHub

React

Active
Storybook GitHub

React Native

Planned
Design Code Accessibility QA
A

Error summary

Displayed at the top of the form when one or more fields fail validation, summarising the errors and linking to the first invalid field

B

Form body

Any form fields added to the form will be rendered here in the main body

C

Submit button

Triggers form submission and validation when activated by the user

  • Minimise cognitive load by using a single column for simpler flows
  • Ensure responsiveness so that when multi-column layouts are used, they gracefully collapse into a single column on smaller devices
  • Validate suitability of multi-column layouts by conducting user research to confirm they're effective for users

Further reading:

<ba-form>
  <ba-input-text label="First Name" name="firstName" hint-text="Your first name" required>
    <p slot="error">Custom required error message</p>
  </ba-input-text>
  <ba-input-text label="Last Name" name="lastName" hint-text="Your last name" required></ba-input-text>

  <ba-button variant="primary">Submit</ba-button>
</ba-form>
Property Attribute Description Type Default
errorHeadingLevel error-heading-level Heading tag for the error message "2" | "3" | "4" | "5" | "6" | undefined '3'
hideErrorMessage hide-error-message Hides the default message boolean | undefined false
  • None
Slot Description Permitted elements
Unnamed slot Elements will render in the body of the component <ba‑button>, <ba‑card>, <ba‑checkbox>, <ba‑checkbox‑card>, <ba‑checkbox‑list>, <ba‑content>, <ba‑flex>, <ba‑form‑group>, <ba‑form‑group‑dropdown>, <ba‑grid>, <ba‑grid>, <ba‑input>, <ba‑input‑date>, <ba‑input‑datepicker>, <ba‑input‑email>, <ba‑input‑number>, <ba‑input‑password>, <ba‑input‑phone‑number>, <ba‑input‑secure‑field>, <ba‑input‑stepper>, <ba‑input‑text>, <ba‑input‑textarea>, <ba‑input‑typeahead>, <ba‑input‑upload>, <ba‑radio‑group>, <ba‑select>, <fieldset>
"error-message" Content for the default error message <ba‑content>
"custom-message" Content for the default error message <ba‑message>

ba-form can be slotted into:

Event Description Type
baFormSubmit Emitted when the form is submitted Event
baFormValid Emitted when the form is validated. Emits out any form data and if the form is valid CustomEvent<FormValid>

Basic example showing how to use the form and trigger validation.

Example of ba-form
<ba-form>
    <ba-input-text label="First Name" name="firstName" required></ba-input-text>
    <ba-input-text label="Last Name" name="lastName" required></ba-input-text>
    <ba-button>Submit</ba-button>
</ba-form>

Example with a custom layout using ba-grid

Custom layout
<ba-form>
  <ba-grid columns-800vw="2">
    <ba-input-text
      label="First Name"
      name="firstName"
    ></ba-input-text>
    <ba-input-text
      label="Last Name"
      name="lastName"
      required
    ></ba-input-text>
  </ba-grid>
  <ba-button>Submit</ba-button>
</ba-form>

Example with a custom error message and a success message.

Custom error and success message
<ba-form>
    <ba-content slot="errorMessage">
      <p>Custom error message</p>
    </ba-content>

    <ba-message variant="success" slot="customMessage" hidden>
      <h3 slot="heading">Form submitted successfully</h3>
      <ba-link href="#" slot="action">Click here to proceed</ba-link>
    </ba-message>

    <ba-input-text label="First Name" name="firstName" required></ba-input-text>
    <ba-input-text label="Last Name" name="lastName"></ba-input-text>

    <ba-button>Submit</ba-button>
</ba-form>

<script>
  document.addEventListener('baFormValid', (e) => {
    if (e.detail.valid) {
      const form = document.querySelector('ba-form')
      form.setAttribute('hide-error-message', '')

      const message = document.querySelector('ba-message')
      message.removeAttribute('hidden')

      // Get the data
      const formData = e.detail.data
    }
  })
</script>

Example of how to perform custom validation.

ba-form with custom validation
<ba-form>
    <ba-input-text label="First Name" name="firstName" required></ba-input-text>
    <ba-input-text label="Last Name" name="lastName"></ba-input-text>

    <ba-button>Submit</ba-button>
</ba-form>

<script>
  document.addEventListener('baFormSubmit', (e) => {
    e.preventDefault()
    //
    // Perform any captcha validation
    //
    // Then perform any custom validation
    //
    // Finally run the forms own validation.
    // This ensures the form picks up on any invalid states that you may have set above.
    // It will also emit out all of the forms data and whether the form is valid.
    form.validateForm()
  })

  // Listener for retrieving the data and checking if the form is valid
  document.addEventListener('baFormValid', (e) => {
    // Check if the form is valid
    if (!e.detail.valid) {
      return
    }

    // Get the form data
    const formData = e.detail.data
  })
</script>

When a form contains multiple buttons, you may not want all of them to submit the form. Ba-form will ignore ba-buttons with the no-submit attribute. Note: because there are multiple ways to submit a form, avoid relying solely on this attribute to prevent form submission; instead, monitor the baFormSubmit event.

A form that includes multiple buttons: a submit button, which allows submission, and a reset button with a no-submit attribute, preventing ba-form from submitting
<ba-form>
  <ba-grid>
    <ba-input-text
      label="First Name"
      name="firstName"
    ></ba-input-text>
    <ba-input-text
      label="Last Name"
      name="lastName"
      required
    ></ba-input-text>
  </ba-grid>
  <ba-button>Submit</ba-button>
  <ba-button variant="secondary" no-submit>Reset</ba-button>
</ba-form>
Prop Description Type Default
errorHeadingLevel Heading tag for the error message "2" | "3" | "4" | "5" | "6" | undefined '3'
hideErrorMessage Hides the default message boolean | undefined false
children Elements will render in the body of the component <ba‑button>, <ba‑card>, <ba‑checkbox>, <ba‑checkbox‑card>, <ba‑checkbox‑list>, <ba‑content>, <ba‑flex>, <ba‑form‑group>, <ba‑form‑group‑dropdown>, <ba‑grid>, <ba‑grid>, <ba‑input>, <ba‑input‑date>, <ba‑input‑datepicker>, <ba‑input‑email>, <ba‑input‑number>, <ba‑input‑password>, <ba‑input‑phone‑number>, <ba‑input‑secure‑field>, <ba‑input‑stepper>, <ba‑input‑text>, <ba‑input‑textarea>, <ba‑input‑typeahead>, <ba‑input‑upload>, <ba‑radio‑group>, <ba‑select>, <fieldset>
errorMessage Content for the default error message <ba‑content>
customMessage Content for the default error message <ba‑message>

None

ba-form can be slotted into:

Events Description
onBaFormSubmit Emitted when the form is submitted
onBaFormValid Emitted when the form is validated. Emits out any form data and if the form is valid

Basic example showing how to use the form and trigger validation.

Example of BaForm
<BaForm>
    <BaInputText label="First Name" name="firstName" required />
    <BaInputText label="Last Name" name="lastName" required />
    <BaButton>Submit</BaButton>
</BaForm>

Example with a custom layout using BaGrid

Custom layout
<BaForm>
  <BaGrid columns800vw="2">
    <BaInputText
      label="First Name"
      name="firstName"
     />
    <BaInputText
      label="Last Name"
      name="lastName"
      required
     />
  </BaGrid>
  <BaButton>Submit</BaButton>
</BaForm>

Example with a custom error message and a success message.

Custom error and success message
function CustomErrorWithSuccess() {
      const [hideErrorMessage, setHideErrorMessage] = useState(false);
      const [showSuccessMessage, setShowSuccessMessage] = useState(false);

      const handleFormValid = (result: {
        isValid: boolean;
        data: Record<string, string>;
      }) => {
        if (result.isValid) {
          setHideErrorMessage(true);
          setShowSuccessMessage(true);
        } else {
          setHideErrorMessage(false);
          setShowSuccessMessage(false);
        }
      };

      return (
        <BaForm
          onBaFormValid={handleFormValid}
          hideErrorMessage={hideErrorMessage}
          customMessage={
            <BaMessage
              variant="success"
              slot="customMessage"
              hidden={!showSuccessMessage}
              heading={<h3 slot="heading">Form submitted successfully</h3>}
              action={<BaLink href="#">Click here to proceed</BaLink>}
            />
          }
        >
          <BaInputText label="First Name" name="firstName" required />
          <BaInputText label="Last Name" name="lastName" required />

          <BaButton>Submit</BaButton>
        </BaForm>
      );
    }

Example of how to perform custom validation.

BaForm with custom validation
function CustomValidationForm() {
      const firstNameRef = useRef<BaInputTextRef>(null);
      const lastNameRef = useRef<BaInputTextRef>(null);

      const handleSubmit = (validate: () => void) => {
        firstNameRef.current?.getValue();
        lastNameRef.current?.getValue();

        // run your custom validation logic here

        validate();
      };

      return (
        <BaForm onBaFormSubmit={handleSubmit}>
          <BaInputText ref={firstNameRef} label="First Name" name="firstName" />
          <BaInputText ref={lastNameRef} label="Last Name" name="lastName" />

          <BaButton>Submit</BaButton>
        </BaForm>
      );
    }

When a form contains multiple buttons, you may not want all of them to submit the form. BaForm will ignore BaButtons with the noSubmit prop. Note: because there are multiple ways to submit a form, avoid relying solely on this attribute to prevent form submission; instead, monitor the onBaFormSubmit event.

A form that includes multiple buttons: a submit button, which allows submission, and a reset button with a noSubmit prop, preventing BaForm from submitting
<BaForm>
  <BaGrid>
    <BaInputText
      label="First Name"
      name="firstName"
     />
    <BaInputText
      label="Last Name"
      name="lastName"
      required
     />
  </BaGrid>
  <BaButton>Submit</BaButton>
  <BaButton variant="secondary" noSubmit>Reset</BaButton>
</BaForm>

React Native documentation coming soon.

It's important to maintain the order of headings so that they appear in a logical order in the page. Screen readers (and other assistive technology) rely on headings to navigate and understand structure of pages.

Further reading:

2 types of errors can be shown when ba-form is used.

  • The first is the individual form field errors. These are shown if the form field's validation fails when calling the validateForm method.
  • The second type is the form error that gets shown at the top of the form. This error only shows if one of the form fields within the form is invalid.

State: Previous element in DOM has focus

⇥ Tab

First focusable element inside the form gets focus

State: A form field inside ba-form has focus

⇥ Tab

Next focusable element inside the form gets focus, or the submit button if it is the last field

State: Submit button has focus

⏎ Enter

Submits the form and triggers validation

␣ Space

Submits the form and triggers validation

State: Error summary has focus after failed submission

⇥ Tab

First focusable element inside the form gets focus

Use this checklist to confirm the component has been configured and used correctly.

  • Use a single column layout for simpler flows to minimise cognitive load
  • When using multi-column layouts, ensure they gracefully collapse into a single column on smaller devices
  • Validate suitability of multi-column layouts by conducting user research to confirm they are effective for users
  • The error summary is only displayed after the user attempts to submit the form
  • The error summary links to the first invalid field in the form
  • Use the error-heading-level attribute to maintain the correct heading hierarchy in the page
  • The error summary heading follows the correct document hierarchy
  • Individual field errors are visible and associated with their respective fields
  • Focus moves to the error summary when the form is submitted with invalid fields
  • ba-form is operable using a keyboard alone

These checks are handled by the component and do not need to be repeated each time it is used.

  • ba-form is operable using assistive technology
  • Error states are communicated to assistive technology
  • Colour contrast for all states in all BAgel themes
  • Animations respect users' reduced motion settings
  • High contrast mode adjustments
Designing Developing Components BAgel helper QA process britishairways.com Careers Cookie policy