govtechsg/sgds-web-component

sgds-forms

Use this skill when users ask about form validation in SGDS, hasFeedback prop, constraint validation, custom validation, noValidate, setInvalid, form submission, or reading FormData from SGDS form components.

First seen Mar 19, 2026

Installation

$ npx skills add govtechsg/sgds-web-component --skill sgds-forms

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from govtechsg/sgds-web-component · top by installs.

npx skills add govtechsg/sgds-web-component

Browse all from govtechsg/sgds-web-component

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 19
License LICENSE
Default branch master
Open issues 65
Status Active

Skill metadata

Parsed from SKILL.md frontmatter.

Version0.0.0
More metadata
author
singapore-design-system
version
0.0.0
audience
external
category
pattern

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 12,043 B
  • docs SUMMARY.md 223 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 205 installs

SKILL.md

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 <form> submission, FormData, and constraint validation automatically.

Prerequisites

See [sgds-components](../sgds-components/SKILL.md) for installation and framework integration.

All form components must be placed inside a <form> element and given a name attribute to participate in form submission.

Quick Decision Guide

Show native HTML validation messages? → Add hasFeedback 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?noValidate prop on <sgds-input>, <sgds-textarea>, <sgds-combo-box>, <sgds-select>, <sgds-datepicker>, <sgds-quantity-toggle>, or <sgds-file-upload>

Disable SGDS validation for the whole form?novalidate on the <form> element

Run 3rd-party validation (e.g. Zod)? → Disable SGDS validation first, then call setInvalid(bool) and set invalidFeedback programmatically

How Validation Is Triggered

SGDS validation is layered:

  1. onChange (after blur) — validates when the user leaves a field; shows feedback if hasFeedback is set
  2. onSubmit — final validation pass fires before submission; blocks the form if any field is invalid
  3. onReset — clears all validity states and field values when the form is reset

Disabled components skip validation entirely.

hintText and Error Message Placement

On most form components (<sgds-input>, <sgds-textarea>, <sgds-select>, <sgds-combo-box>, <sgds-quantity-toggle>, <sgds-file-upload>), hintText and the error message share the same space below the input container:

  • When the field is invalid (and hasFeedback is set), the error message replaces hintText.
  • When the error is resolved, hintText reappears.

<sgds-checkbox-group> and <sgds-radio-group> behave differently — hintText 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 hasFeedback, but the visible error message only renders when hasFeedback is present. The hasFeedback prop accepts:

Value Behaviour
hasFeedback (boolean) Shows the native HTML validation message as error text
hasFeedback="text" Same as boolean — shows text feedback only
hasFeedback="style" Shows invalid border/colour styling only, no text
hasFeedback="both" Shows both invalid styling and text feedback

You can also override the displayed message with invalidFeedback:

<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
<sgds-input> required, pattern, min, max, minlength, maxlength
<sgds-textarea> required, minlength, maxlength
<sgds-quantity-toggle> min, max
<sgds-datepicker> required, minDate, maxDate
<sgds-select> required
<sgds-combo-box> required
<sgds-radio-group> required
<sgds-checkbox-group> required
<sgds-checkbox> (standalone) required
<sgds-file-upload> required (file size limit WIP)

Full Form Example

<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 FormData API in the submit handler. Access sgds-file-upload files separately via the component's selectedFiles property:

<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 setInvalid(bool) and update invalidFeedback in response to input events.

Option 1 — Disable per component with noValidate

Currently supported on <sgds-input>, <sgds-textarea>, <sgds-combo-box>, <sgds-datepicker>, <sgds-file-upload>, <sgds-select>, <sgds-quantity-toggle>, <sgds-radio-group>, <sgds-checkbox>, and <sgds-checkbox-group>.

<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 novalidate

Adding novalidate to the <form> element disables constraint validation and SGDS validation for all child components. Then apply setInvalid logic per field.

<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>

setInvalid(bool) Method

Parameter Type Description
bool boolean true marks the component invalid and shows invalidFeedback; false clears the invalid state

Pair setInvalid(true) with setting the invalidFeedback property on the element to control the displayed message.

Custom Validation Support Status

Component Status
<sgds-input> ✅ Implemented
<sgds-textarea> ✅ Implemented
<sgds-datepicker> ✅ Implemented
<sgds-checkbox> / <sgds-checkbox-group> ✅ Implemented
<sgds-quantity-toggle> ✅ Implemented
<sgds-radio-group> ✅ Implemented
<sgds-file-upload> ✅ Implemented
<sgds-select> ✅ Implemented
<sgds-combo-box> ✅ Implemented

For AI agents

  1. Always place SGDS form components inside a <form> element with name attributes for them to participate in form submission and FormData.
  2. hasFeedback must be present on a form component for the error message to visually appear — constraint validation alone does not show UI feedback.
  3. To show both the invalid border style and the error message text, use hasFeedback="both". A plain boolean hasFeedback shows the message text only.
  4. Use invalidFeedback to override the browser's native constraint validation message with a custom string.
  5. Constraint validation and form submission blocking are built-in — do not add extra submit event listeners to replicate this behaviour.
  6. Use new FormData(event.target) in the submit handler to read values. For file uploads, read selectedFiles directly from the <sgds-file-upload> element.
  7. When using custom/3rd-party validation, add noValidate on the component (or novalidate on the form), then call element.setInvalid(true/false) and set element.invalidFeedback inside the relevant event listener (sgds-input for inputs/textareas, sgds-change-date for datepicker, sgds-change for select/combo-box/radio-group/checkbox-group/quantity-toggle, sgds-add-files/sgds-remove-file for file-upload).
  8. All form components (<sgds-input>, <sgds-textarea>, <sgds-datepicker>, <sgds-combo-box>, <sgds-select>, <sgds-quantity-toggle>, <sgds-radio-group>, <sgds-checkbox>, <sgds-checkbox-group>, and <sgds-file-upload>) fully support custom validation via noValidate + setInvalid.
  9. The reset button (type="reset") automatically clears all validity states and values — no extra reset logic is needed.
  10. Disabled components are excluded from constraint validation and will never block form submission.