FormGroup

Context provider and optional layouting element for form fields.

Automatically pairs the label (for) with the field (id) using a randomly generated ID.

Help text

Config

The options below are to be set in the global configuration at the following location:

{
  components: {
    formGroup: { ... },
  },
}

as

Define the type of HTML element it should be rendered as.

  • type: String
  • default: null (renderless)

WARNING

It’s recommended to specify a value when using server-side rendering (SSR), especially if you render components conditionally within the form group, as it can result in IDs pairing mismatches.

Example

as: 'div',

class

Classes applied to the root element, except when the prop bare is set to true. See styling components.

  • type: Array<String | Object> | Object | String | (props: Object) => {}
  • default: null

WARNING

Since this component is renderless by default, classes won’t be applied. You need to define the as option above or set the component as prop to render an element on which classes will be applied.

Example

class({ required, disabled }) {
    return ['form-group', {
        'form-group--required': required,
        'form-group--disabled': disabled
    }]
}

API

NameTypeDefaultDescription
Props
variant string|arraynull

Useful when used in the component config class option, to style it conditionally. See styling components.

bare booleanfalse

Disable class inheritance from the component config. See styling components.

as string|falsenull

Specify an HTML element to render. If you defined a default value in the config, you can set it to false to render only its children (renderless).

required booleannull

Flag the group as required. Automatically sets the attribute of the same name on any child CTextField, CTextarea, CRadio & CCheckbox.

disabled booleannull

Flag the group as disabled. Automatically sets the attribute of the same name on any child CTextField, CTextarea, CRadio, CCheckbox & CSelect.

readonly booleannull

Flag the group as readonly. Automatically sets the attribute of the same name on any child CTextField & CTextarea.

Slots
default
Binding nameTypeDescription
requiredboolean

The required prop value

disabledboolean

The disabled prop value

readonlyboolean

The readonly prop value

idsobject

Dynamically generate an id based on the key used, for example: ids.field will return chusho-field-{form group id}. The same key will always return the same id. The label default id is ids.label and the field default id is ids.field.

Examples

Renderless

By default, CFormGroup is renderless, which means it doesn’t create any HTML element. It only renders it’s children.

<CFormGroup required>
  <CLabel>Label</CLabel>
  <CTextField />
</CFormGroup>

The code above will output this:

<!--[-->
<label for="chusho-field-{uid}" id="chusho-label-{uid}">Label</label>
<input type="text" id="chusho-field-{uid}" required />
<!--]-->

Notice how the required prop set on the CFormGroup was added to the input automatically.

As element

You can force it to render the element of your choice using the as prop (or set a default value in the config, so it always render one; see config above).

<CFormGroup as="div" class="form-group" disabled>
  <CLabel>Label</CLabel>
  <CTextField />
</CFormGroup>

The code above will output this:

<div class="form-group">
  <label for="chusho-field-{uid}" id="chusho-label-{uid}">Label</label>
  <input type="text" id="chusho-field-{uid}" disabled />
</div>

Using slot scope

Flags are accessible in the default slot scope, allowing you to render other elements conditionaly based on them:

<CFormGroup v-slot="{ required }" :required="someVariableOnYourComponent">
  <CLabel>
    Label <abbr v-if="required" title="Required" aria-label="Required">*</abbr>
  </CLabel>
  <CTextField />
</CFormGroup>

Using ids generator for extra elements

Label and field are automatically linked together by default. Here we use the ids generator provided by the form group to generate an extra id to describe the field with an additional help text.

<CFormGroup v-slot="{ ids }">
  <CLabel>Password</CLabel>
  <CTextField type="password" :aria-describedby="ids.help" />
  <div :id="ids.help">Help text describing the field expected value.</div>
</CFormGroup>

The code above will output this:

<!--[-->
<label for="chusho-field-{uid}" id="chusho-label-{uid}">Password</label>
<input
  type="password"
  id="chusho-field-{uid}"
  aria-describedby="chusho-help-{uid}"
/>
<div id="chusho-help-{uid}">Help text describing the field expected value.</div>
<!--]-->

Nested

You shouldn’t have multiple labels or fields in a single CFormGroup. When you want to group related fields, like checkboxes or radios, you should give each field its own CFormGroup.

<template>
  <!-- The `required` flag will be applied on all checkboxes -->
  <CFormGroup v-slot="{ ids }" required>
    <CLabel>Fruits</CLabel>
    <!-- Label the group of checkboxes with the CLabel value above -->
    <ul :aria-labelledby="ids.label">
      <CFormGroup v-for="fruit in fruits" :key="fruit" as="li">
        <CLabel>
          <CCheckbox v-model="state[fruit]" />
          {{ fruit }}
        </CLabel>
      </CFormGroup>
    </ul>
  </CFormGroup>
</template>

<script setup>
import { reactive } from 'vue';

const fruits = ['banana', 'apple', 'kiwi'];
const state = reactive({
  banana: true,
  apple: false,
  kiwi: false,
});
</script>