SGDS Form Validation Pattern
SGDS form components integrate with the browser's
ElementInternals API so they behave like native HTML form controls — they participate in
submission,
, and constraint validation automatically.
Prerequisites
See sgds-components for installation and framework integration.
All form components must be placed inside a
element and given a
attribute to participate in form submission.
Quick Decision Guide
Show native HTML validation messages? → Add
to each form component
Prevent submit when fields are invalid? → Built-in — no extra code needed
Read submitted field values? → Use
new FormData(event.target)
in the submit handler
Disable SGDS validation per component? →
prop on
or
(others WIP)
Disable SGDS validation for the whole form? →
on the
element
Run 3rd-party validation (e.g. Zod)? → Disable SGDS validation first, then call
and set
programmatically
How Validation Is Triggered
SGDS validation is layered:
- onChange (after blur) — validates when the user leaves a field; shows feedback if is set
- onSubmit — final validation pass fires before submission; blocks the form if any field is invalid
- onReset — clears all validity states and field values when the form is reset
Disabled components skip validation entirely.
and Error Message Placement
On most form components (
,
,
,
,
,
),
and the error message
share the same space below the input container:
- When the field is invalid (and is set), the error message replaces .
- When the error is resolved, reappears.
and
behave differently —
is rendered in the label row above the options and remains visible at all times. The error message appears separately below the options.
Activating Feedback Display
Constraint validation runs regardless of
, but the
visible error message only renders when is present. The
prop accepts:
| Value | Behaviour |
|---|
| (boolean) | Shows the native HTML validation message as error text |
| Same as boolean — shows text feedback only |
| Shows invalid border/colour styling only, no text |
| Shows both invalid styling and text feedback |
You can also override the displayed message with
:
html
<sgds-input
name="email"
label="Email"
type="email"
required
hasFeedback="both"
invalidFeedback="Please enter a valid email address"
></sgds-input>
Constraint Validations by Component
| Component | Supported constraints |
|---|
| , , , , , |
| , , |
| , |
| , , |
| |
| |
| |
| |
| (standalone) | |
| (file size limit WIP) |
Full Form Example
html
<form id="my-form" class="d-flex-column">
<sgds-input
label="First Name"
name="firstName"
required
hasFeedback="both"
pattern="[A-Za-z ]+"
invalidFeedback="Letters only"
></sgds-input>
<sgds-datepicker
label="Appointment Date"
name="appointmentDate"
required
hasFeedback
></sgds-datepicker>
<sgds-radio-group label="Gender" name="gender" required hasFeedback>
<sgds-radio value="female">Female</sgds-radio>
<sgds-radio value="male">Male</sgds-radio>
</sgds-radio-group>
<sgds-checkbox-group
label="Food Preference"
name="food"
required
hasFeedback
hintText="Select at least one option"
>
<sgds-checkbox value="vegetarian">Vegetarian</sgds-checkbox>
<sgds-checkbox value="halal">Halal</sgds-checkbox>
</sgds-checkbox-group>
<sgds-textarea
label="Comments"
name="comments"
required
minlength="3"
hasFeedback
></sgds-textarea>
<sgds-button type="submit">Submit</sgds-button>
<sgds-button type="reset" variant="ghost">Reset</sgds-button>
</form>
Reading Form Values via FormData
Use the native
API in the submit handler. Access
files separately via the component's
property:
html
<script>
const form = document.getElementById("my-form");
form.addEventListener("submit", event => {
event.preventDefault();
const formData = new FormData(event.target);
const firstName = formData.get("firstName");
const gender = formData.get("gender");
const comments = formData.get("comments");
// File upload files are not in FormData automatically
const fileUpload = document.querySelector("sgds-file-upload");
for (let i = 0; i < fileUpload.selectedFiles.length; i++) {
formData.append("file" + i, fileUpload.selectedFiles[i]);
}
// Submit formData to server
});
</script>
Custom Validation (Disabling SGDS Validation)
For 3rd-party validation libraries (e.g. Zod) or fully custom logic, disable SGDS's built-in validation first, then call
and update
in response to input events.
Option 1 — Disable per component with
Currently supported on
and
only. Other components are WIP.
html
<sgds-input
noValidate
id="keys-input"
name="keys"
label="Keys"
hintText="Cannot start with special characters"
hasFeedback="both"
></sgds-input>
<script>
const input = document.getElementById("keys-input");
input.addEventListener("sgds-input", e => {
if (/^[^a-zA-Z0-9]/.test(e.target.value)) {
e.target.setInvalid(true);
e.target.invalidFeedback = "Keys cannot start with special characters";
} else {
e.target.setInvalid(false);
}
});
</script>
Option 2 — Disable at form level with
Adding
to the
element disables constraint validation and SGDS validation for
all child components. Then apply
logic per field.
html
<form id="custom-form" novalidate class="d-flex-column">
<sgds-input
id="keys-input"
name="keys"
label="Keys"
hasFeedback="both"
></sgds-input>
<sgds-textarea
id="bio-textarea"
name="bio"
label="Bio"
hasFeedback
></sgds-textarea>
</form>
<script>
document.getElementById("keys-input").addEventListener("sgds-input", e => {
if (/^[^a-zA-Z0-9]/.test(e.target.value)) {
e.target.setInvalid(true);
e.target.invalidFeedback = "Invalid key format";
} else {
e.target.setInvalid(false);
}
});
document.getElementById("bio-textarea").addEventListener("sgds-input", e => {
if (e.target.value.length < 10) {
e.target.setInvalid(true);
e.target.invalidFeedback = "Bio must be at least 10 characters";
} else {
e.target.setInvalid(false);
}
});
</script>
Method
| Parameter | Type | Description |
|---|
| | marks the component invalid and shows ; clears the invalid state |
Pair
with setting the
property on the element to control the displayed message.
Custom Validation Support Status
| Component | Status |
|---|
| ✅ Implemented |
| ✅ Implemented |
| / | WIP |
| WIP |
| WIP |
| WIP |
| WIP |
| WIP |
| WIP |
For AI agents
- Always place SGDS form components inside a element with attributes for them to participate in form submission and .
- must be present on a form component for the error message to visually appear — constraint validation alone does not show UI feedback.
- To show both the invalid border style and the error message text, use . A plain boolean shows the message text only.
- Use to override the browser's native constraint validation message with a custom string.
- Constraint validation and form submission blocking are built-in — do not add extra submit event listeners to replicate this behaviour.
- Use
new FormData(event.target)
in the submit handler to read values. For file uploads, read directly from the element.
- When using custom/3rd-party validation on or , add on the component (or on the form), then call
element.setInvalid(true/false)
and set inside the event listener.
- Only and fully support custom validation via + . All other form components have this feature as WIP.
- The reset button () automatically clears all validity states and values — no extra reset logic is needed.
- Disabled components are excluded from constraint validation and will never block form submission.