Actions
Run mutations on click. Actions receive the context object and a data object.
<!-- Simple action, no data needed -->
<button gg-action="sign_out">Sign out</button>
<!-- Action with explicit data (csv form) -->
<button gg-action="update_status" gg-action-data="status:archived">Archive</button>
<!-- One attribute per field — handy in Webflow where only the value varies -->
<button gg-action="update_status" gg-action-data-status="archived">Archive</button>
<button gg-action="update_status" gg-action-data-status="active">Restore</button>
<!-- Inside a data list, the action automatically receives the row's record -->
<ul gg-data-list="posts_list">
<li gg-list-template>
<h3 gg-field="title"></h3>
<button gg-action="delete_post">Delete</button>
</li>
</ul>When an action is inside a gg-data or gg-data-list container, it automatically receives the record as its data. Explicit gg-action-data values are merged on top (and win on conflict).
Two ways to declare explicit data, and they can be mixed:
gg-action-data="k1:v1,k2:v2"— comma-separatedname:valuepairs, key casing preserved.gg-action-data-{name}="value"— one attribute per field. Names are lowercased by the browser, so prefer the csv form when you need camelCase keys. On key collisions, prefixed attrs win over the csv form.
Handler signature
Action functions should return { ok: true } or { ok: false, error }:
app.addAction("delete_post", async ({ sb }, { id }) => {
const { error } = await sb.from("posts").delete().eq("id", id);
return error ? { ok: false, error } : { ok: true };
});Handlers receive (context, data, params, helpers). params is a URLSearchParams snapshot of the current URL. helpers exposes utilities scoped to the trigger — see below.
Removing a list item
When an action triggered from inside a gg-data-list row succeeds, helpers.removeItem(predicate) removes the matching clone(s) from that list without refetching:
app.addAction("delete_post", async ({ sb }, { id }, _params, helpers) => {
const { error } = await sb.from("posts").delete().eq("id", id);
if (error) return { ok: false, error };
helpers.removeItem((record) => record.id === id);
return { ok: true };
});How it works:
- The action engine captures the trigger's enclosing
gg-data-listcontainer before invoking the handler.removeItemwalks that container's rendered rows and removes every clone whose__ggRecordmatches the predicate. - The predicate runs against
__ggRecord(the row's source record), so you can match on any field —id,slug, compound keys, etc. - If the trigger isn't inside a list (e.g. a global action button),
removeItemwarns and no-ops.
If a transition is configured on init(), the removed clone fades out using the same duration/easing as gg-visible-when and switcher cases before detaching; otherwise removal is instant.
removeItem is independent from app.invalidate(). Call removeItem for optimistic local updates after a delete; call invalidate when you need every subscribed list/detail view to refetch from the server. The two compose — you can do both if a delete affects multiple lists and you only want to fast-path the one the user clicked in.
See Loading and confirm for the corresponding loading-state and confirmation prompt attributes.