with one click
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.
Menu
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.
Complete reference for all SGDS web components including installation and framework integration. Use when users ask about any <sgds-*> component — accordion, alert, badge, breadcrumb, button, card, checkbox, close-button, combo-box, datepicker, description-list, divider, drawer, dropdown, file-upload, footer, icon, icon-button, icon-card, icon-list, image-card, input, link, mainnav, masthead, modal, overflow-menu, pagination, progress-bar, quantity-toggle, radio, select, sidebar, sidenav, skeleton, spinner, stepper, subnav, switch, system-banner, tab, table, table-of-contents, textarea, thumbnail-card, toast, or tooltip. Also covers React 19+, React ≤18, Vue, Angular, and Next.js integration.
Complete reference for all SGDS utility classes with the sgds: prefix. Use when users ask about setup, background-color, text-color, border-color, border-width, border-radius, typography, spacing, grid, dimension, opacity, color-semantics, or any sgds: Tailwind utility class. Also covers Tailwind v4 imports, theme switching, and framework integration for utilities.
Complete catalog of page layout patterns for SGDS applications. Use this skill whenever a user asks about page layouts, content arrangement, aside panels, split views, sidebar layouts, breadcrumb layouts, or viewport-height layouts — even if they just say 'how should I lay out my page' or 'I need a two-column layout'. Covers Full Width layouts (public-facing pages with sgds-container) and With Sidebar layouts (dashboards/internal tools with sgds-container-sidebar). Trigger on: layout, aside, split view, sidebar layout, two-column, three-column, content arrangement, page structure with aside.
Writes Storybook stories following the templates folder pattern with automatic file concatenation. Use when creating or updating component stories in stories/component-templates/, organizing story variants, or documenting component usage in Storybook.
Reusable UI blocks for building full SGDS pages and sections. Use this skill whenever the user wants to build pages, design layouts, create sections, or compose multiple sections together. Trigger on ANY mention of: app layout, application shell, page structure, hero sections, CTAs (call-to-action), cards, card grids, feature sections, product benefits, statistics displays, metrics, page headers, page titles, filter interfaces, search filters, data tables, forms, landing pages, dashboards, sidebar navigation, or any major page component — even if they don't name it a 'block'. Also use for: 'I need a filter', 'build a form', 'create a call to action', 'design a landing page', 'show statistics', 'sidebar layout', or similar requests for page-level UI. These are drop-in sections and shell structures, not full pages. Compose them with sgds-templates to build complete pages.
Starting point for any new application built with the SGDS web component library. Apply this skill first whenever a user is bootstrapping a new SGDS project, setting up a new app, or asking where to begin with SGDS. Covers font setup, foundation CSS, utilities, components, and app layout in the correct order.
| name | sgds-forms |
| description | 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. |
| metadata | {"author":"singapore-design-system","version":"0.0.0","audience":"external","category":"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.
See sgds-components 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.
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> or <sgds-textarea> (others WIP)
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
SGDS validation is layered:
hasFeedback is setDisabled components skip validation entirely.
hintText and Error Message PlacementOn 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:
hasFeedback is set), the error message replaces hintText.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.
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>
| 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) |
<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>
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>
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.
noValidateCurrently supported on <sgds-input>, <sgds-textarea>, <sgds-combo-box> and **<sgds-datepicker>. Other components are WIP.
<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>
novalidateAdding 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.
| Component | Status |
|---|---|
<sgds-input> | ✅ Implemented |
<sgds-textarea> | ✅ Implemented |
<sgds-datepicker> | ✅ Implemented |
<sgds-checkbox> / <sgds-checkbox-group> | WIP |
<sgds-quantity-toggle> | WIP |
<sgds-radio-group> | WIP |
<sgds-file-upload> | WIP |
<sgds-select> | WIP |
<sgds-combo-box> | WIP |
<form> element with name attributes for them to participate in form submission and FormData.hasFeedback must be present on a form component for the error message to visually appear — constraint validation alone does not show UI feedback.hasFeedback="both". A plain boolean hasFeedback shows the message text only.invalidFeedback to override the browser's native constraint validation message with a custom string.new FormData(event.target) in the submit handler to read values. For file uploads, read selectedFiles directly from the <sgds-file-upload> element.<sgds-input>, <sgds-textarea>, or <sgds-datepicker>, 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-input>, <sgds-textarea>, and <sgds-datepicker> fully support custom validation via noValidate + setInvalid. All other form components have this feature as WIP.type="reset") automatically clears all validity states and values — no extra reset logic is needed.