JB Design System form coordinator for validation, dirty checks, value collection, virtual fields, and nested forms.
- Validates JB form controls with one command.
- Tracks aggregate dirty state.
- Gets and sets values for named child controls.
- Supports native form controls, form-associated custom elements, virtual elements, and nested
jb-formelements. - Dispatches submit, dirty-change, and validity-change events.
Use jb-form when a group of fields needs aggregate validation, value collection, dirty-state tracking, virtual fields, or nested form sections. The basic setup is shown in the normal form demo.
Use a native <form> when you only need browser-native submission and do not need JB aggregate helpers.
See the React documentation.
Other integrations: Angular · Vue · Nuxt · Svelte · SvelteKit · SolidJS · Lit · Next.js · Astro · Blazor · Server-rendered templates · WordPress · Alpine.js and HTMX
npm i jb-formimport 'jb-form';<jb-form>
<jb-input name="name" required></jb-input>
<jb-button type="submit">Submit</jb-button>
</jb-form>| name | type | default | description |
|---|---|---|---|
name |
string |
"" |
Name used when this form is nested inside another jb-form. Demo |
| name | type | readonly | description |
|---|---|---|---|
value |
FormValues |
no | Aggregated object of named child values. Setting it calls setFormValues(value). Demo |
name |
string |
no | Name attribute value used by parent jb-form traversal. Demo |
isDirty |
boolean |
yes | true when any named child control, virtual element, or sub-form is dirty. Demo |
validation |
ValidationHelper<FormValues> |
yes | Aggregate jb-validation helper. Demo |
validElements |
HTMLElement[] |
yes | Connected native and custom form controls registered directly under this jb-form. Demo |
virtualElements |
object | yes | Virtual element registry: list, dictionary, add(config), and remove({ virtualElement }). Demo |
subForms |
object | yes | Nested jb-form registry: list and dictionary. Demo |
formElements |
FormElements |
yes | Internal native/custom child form element registry. Demo |
| name | returns | description |
|---|---|---|
checkValidity() |
boolean |
Runs synchronous aggregate validation without showing errors. Demo |
reportValidity() |
boolean |
Runs synchronous aggregate validation and asks children to show errors. Demo |
jbCheckValidity({ showError }) |
Promise<CheckValidityAsyncResult> |
Runs rich async validation for jb-validation compatible children, virtual elements, and sub-forms. Demo |
getValidationMessages() |
FormValidationMessages |
Returns validation messages for named child controls, virtual elements, and sub-forms. Demo |
getValidationSummary() |
FormValidationSummary |
Returns validation summaries for named jb-validation compatible items. Demo |
getValidationResult() |
FormValidationResult |
Returns full validation results for named jb-validation compatible items. Demo |
getFormValues() |
FormValues |
Returns all named child values. Repeated names become TraverseCollection. Demo |
getFormDirtyStatus() |
TraverseResult<boolean> |
Returns dirty status for named child controls, virtual elements, and sub-forms. Demo |
setFormValues(value, shouldUpdateInitialValue?) |
void |
Sets values by name. Also updates initial values unless the second argument is false. Demo |
setFormInitialValues(value, shouldUpdateValue?) |
void |
Sets initial values used for dirty checks. Also updates current values unless the second argument is false. Demo |
reset() |
void |
Restores custom controls, native controls, virtual elements, and nested forms to their initial values and clears validation state. Demo |
| event | detail | description |
|---|---|---|
submit |
none | Dispatched after a trusted child submit is intercepted and reportValidity() returns true. Demo |
dirty-change |
{ isDirty: boolean } |
Dispatched when aggregate dirty state changes. Demo |
validity-change |
{ isValid: boolean } |
Dispatched when aggregate synchronous validity changes. Demo |
change |
none | Dispatched by the form when a virtual element changes. Child controls may also bubble their own change events through jb-form. Demo |
form-change |
none | Dispatched from a child control when setFormValues() changes it programmatically. Demo |
init |
none | Dispatched from connectedCallback after child element scanning starts. Demo |
disconnect |
none | Dispatched from disconnectedCallback. Demo |
Use checkValidity() for a silent synchronous check and reportValidity() to show child validation messages; compare both flows in the imperative methods demo.
const form = document.querySelector('jb-form');
const isValid = form.checkValidity();
const isValidAndShown = form.reportValidity();Use jbCheckValidity() when async validations are involved. It validates JB validation-compatible child controls, virtual elements, and sub-forms and returns a tree result with element references, as shown in the invalid-element demo.
import { getInvalidElements } from 'jb-form';
const result = await form.jbCheckValidity({ showError: true });
const invalidElements = getInvalidElements(result);Detailed validation helpers are exercised in the form validation demo:
form.getValidationMessages();
form.getValidationSummary();
form.getValidationResult();jb-form collects values from named direct child controls, virtual elements, and named sub-forms. See the value collection demo for direct reads and updates.
const form = document.querySelector('jb-form');
const values = form.getFormValues();
form.setFormValues({
name: 'Joe',
age: 10,
});
form.setFormValues({ name: 'Joe' }, false);setFormValues(value) updates both value and initialValue by default. Pass false as the second argument when you only want to change current values; the imperative methods demo shows both options.
form.setFormInitialValues({ name: 'Joe', age: 10 });
form.setFormInitialValues({ name: 'Joe' }, false);Reset the complete form tree with reset():
The imperative methods demo and nested form demo show aggregate dirty state in action.
form.reset();console.log(form.isDirty);
console.log(form.getFormDirtyStatus());
form.addEventListener('dirty-change', (event) => {
console.log(event.detail.isDirty);
});jb-form listens for trusted submit events from submit-capable child controls, prevents the original event, calls reportValidity(), and dispatches its own submit event when the form is valid. Try the complete flow in the form test demo.
form.addEventListener('submit', (event) => {
event.preventDefault();
console.log(form.getFormValues());
});Use virtual elements for state that is not represented by a JB/native form control but still needs to participate in form values, dirty checks, or validation. The value demo includes virtual value participation.
import { ValidationHelper } from 'jb-validation';
const tagList = form.virtualElements.add({
name: 'tags',
validation: new ValidationHelper({
getValue: () => tags,
getValidations: () => [
{
validator: (value) => value.length > 0,
message: 'Select at least one tag',
},
],
}),
getValue: () => tags,
getDirtyStatus: () => tags.length !== initialTags.length,
setValue: (value) => {
tags = value;
},
setInitialValue: (value) => {
initialTags = value;
},
reset: () => {
tags = [...initialTags];
},
});
tagList.dispatchOnChange();Remove a virtual element when it is no longer part of the form:
form.virtualElements.remove({ virtualElement: tagList });When two or more named items share the same name, the value becomes a TraverseCollection, which extends Map and is marked with ValueCollectionSymbol. See the same-name demo and the value collection documentation.
<jb-form>
<jb-input name="personName" value="Ali"></jb-input>
<jb-input name="phoneNumber" value="1234"></jb-input>
<jb-input name="phoneNumber" value="5678"></jb-input>
</jb-form>const values = form.getFormValues();
console.log(values.phoneNumber instanceof Map); // trueIf fields have id, the collection uses the id as the key; otherwise it uses numeric keys.
Nested jb-form elements can be managed individually and by a parent form, as shown in the form tree demo.
<jb-form id="parentForm">
<jb-form name="profile">
<jb-input name="firstName"></jb-input>
</jb-form>
<jb-form name="security">
<jb-input name="password"></jb-input>
</jb-form>
</jb-form>console.log(document.querySelector('#parentForm').getFormValues());jb-form has a default slot for child controls and layout content. It does not currently expose CSS parts or CSS variables; the normal form demo shows the default slot layout.
- See
jb-form/reactif you want to use this component in React. - See All JB Design System Component List for more components.
- Use Contribution Guide if you want to contribute to this component.
- Import
jb-formonce before using<jb-form>. - Put fields inside the default slot and give each field a
nameif its value should be collected. - Use
getFormValues()for aggregate values andsetFormValues(values)to update children by name. - Use
jbCheckValidity({ showError: true })for async validation;checkValidity()andreportValidity()are synchronous. - Listen to
submit,dirty-change, andvalidity-changefor form workflows. - Repeated names return
TraverseCollection, not a plain array. - Use
virtualElements.add(config)for non-DOM or non-standard form state. - This package includes
custom-elements.jsonand points to it with the package.jsoncustomElementsfield. The field is documented by the Custom Elements Manifest project in Referencing manifests from npm packages. - In
custom-elements.json,exports.kind: "js"describes JavaScript/TypeScript exports andexports.kind: "custom-element-definition"maps thejb-formtag name toJBFormWebComponent.