Forms
Override a form's submit to run a registered handler instead of the browser default. The handler receives the form's FormData directly.
Form actions
html
<form gg-form-action="create_post">
<input name="title" required />
<textarea name="body"></textarea>
<button type="submit">Save</button>
</form>js
app.addFormAction("create_post", async ({ sb }, formData) => {
const { error } = await sb.from("posts").insert({
title: formData.get("title"),
body: formData.get("body"),
});
return error ? { ok: false, error } : { ok: true };
});The handler receives (context, formData, params). preventDefault is called automatically — the form will not submit to its action URL. Return { ok: true } or { ok: false, error }.
Validation errors
Form actions can return validation errors and the engine will display them via attributes on your markup.
js
app.addFormAction("create_post", async ({ sb }, formData) => {
const title = formData.get("title");
if (!title) {
return {
ok: false,
field_errors: [{ name: "title", message: "Title is required" }],
};
}
const { error } = await sb.from("posts").insert({ title });
return error
? { ok: false, error: "Could not save — please try again." }
: { ok: true };
});Markup:
html
<form gg-form-action="create_post">
<input name="title" />
<p gg-form-field-error="title"></p>
<textarea name="body"></textarea>
<p gg-form-field-error="body"></p>
<!-- Optional: list every field error in one place -->
<ul gg-form-error-list>
<li gg-list-template>
<strong gg-field="name"></strong>: <span gg-field="message"></span>
</li>
</ul>
<!-- Optional: form-level error (the result.error string) -->
<p gg-form-error></p>
<button type="submit">Save</button>
</form>What the engine does:
- Sets
gg-form-field-invalid="true"on each invalid input — target with CSS likeinput[gg-form-field-invalid="true"] { border-color: red; }. - Sets the
textContentof[gg-form-field-error="<name>"]elements to the matching message. - Populates
[gg-form-error-list]using the same template pattern asgg-data-list(clones[gg-list-template], appliesgg-fieldbindings). - Sets the
textContentof[gg-form-error]to the top-levelerrorstring. - All errors are cleared at the start of each submit, and a field's invalid attr + message are cleared when the user types in that field.
Form visibility
Conditionally show/hide elements based on form field values.
html
<form>
<label><input type="radio" name="reason" value="scheduling" /> Scheduling</label>
<label><input type="radio" name="reason" value="other" /> Other</label>
<div gg-visible-when="reason:other">
<input type="text" name="other_reason" placeholder="Please specify" />
</div>
</form>Hidden elements get display: none, inert, and aria-hidden="true". Transitions are a 200ms opacity fade. Use gg-form-scope on a non-<form> ancestor when closest("form") can't reach the inputs (e.g. shadow DOM).