{"id":19408,"plugin_id":"plugins_6a986dc5b2d88191aa3fbe81033fb978","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T23:15:33.176Z","digest":"a8b8bd690ef33e072362e1ef6840c62a338b5802c83e0b9028117d0f6785c4cf","against":null,"payload":{"description":"Use when adding a form (like contact, signup, newsletter, lead-capture or survey) to a Uniform project, when creating form field component definitions or building a generic form submission endpoint. Do not use when embedding an iframe for an external form provider.","included_files":[{"relative_path":"agents/openai.yaml","size_in_bytes":163},{"relative_path":"references/modeling.md","size_in_bytes":7875},{"relative_path":"references/submission.md","size_in_bytes":2604}],"name":"uniform-forms","skill_md_contents":"---\nname: uniform-forms\ndescription: >-\n  Use when adding a form (like contact, signup, newsletter, lead-capture or survey) to a\n  Uniform project, when creating form field component definitions or building a generic form submission endpoint.\n  Do not use when embedding an iframe for an external form provider.\nlicense: MIT\n---\n\n# Uniform forms\n\nA form in Uniform is not a special entity — it is a **composition of components**. A `Form` container component renders the `<form>` element and owns the resulting submit action; each input is its own component placed in the container's slot. Authors assemble forms in Uniform's visual editor; developers own the markup, state, and submission.\n\n## Architecture\n\n```\nform                       container: renders <form>, owns state, submits\n├── slot: formFields       formTextField, formTextAreaField, formSelectField,\n│                          formCheckboxField, formRadioField (+ $personalization, $test)\n└── slot: formButtons      formButton (min 1) (+ $personalization, $test)\n\nformSelectField / formRadioField\n└── slot: options          formSelectOption, formRadioOption (+ $personalization, $test)\n```\n\nField components **self-register** with that context on mount and read/write their own value through it. The container never inspects its slot contents.\n\n## Author-set Field name and identifier\n\n**Every field must have a stable, author-set identifier.** The `name` parameter is required on every field component. Slugify it to get the identifier used for the HTML `name`/`id`, and the payload key. **Never fall back to a generated identifier, warn in the Uniform's visual editor when a field is missing an identifier**.\n\n## Form Submit or Fields should self-register\n\n**Do not introspect slots.** Do not walk `component.slots.formFields` to build fields. That code can easily miss fields nested inside `$personalization` or `$test` wrappers, and duplicates the slot's structure in two places. Use the form submit event to gather the data or self-register each field based on the component's nested context.\n\n## Options are a slot, not a `$block` parameter\n\nDropdown and radio options are components in an `options` slot. This is good practice for these components as it allows them to be personalized, be A/B tested, or support visibility conditions. Do not use blocks or a `$block` parameter as **it cannot be created or edited via MCP or AI**, so a block-based model cannot be built or maintained by an agent.\n\n## Restrict every slot explicitly\n\n**Never set `allowAllComponents: true`.** List the field components, and add `$personalization` and `$test` to `formFields` so individual fields can be optimized.\n\n## Always validate on the server\n\nClient-side validation, `required`, `minlength`, `maxlength`, `pattern` and other built-in form input parameters drive the HTML validation UX only. Re-check every required field in the API route.\n\n## Keep track of the form submit state\n\nTrack `idle | submitting | success | error`. Disable the submit button while submitting and render the result in the page with `aria-live`. Never use `alert()` or `confirm()`.\n\n## Build order\n\n1. **Model the components** in Uniform — `form` first, then fields, then `formButton`, then `formSelectOption`. See [modeling.md](references/modeling.md).\n2. **Build a single API route** to receive submissions according to the payload contract. See [submission.md](references/submission.md).\n3. **Build the form submission logic** to send submissions according to the payload contract.\n5. **Build all components** in the current tech stack and ensure the `<form>` submit event triggers the form submission logic.\n6. **Assemble a form in Uniform's visual editor** and submit it to confirm the payload keys match the authored field names.\n\n## Payload contract\n\nThe container POSTs this to `/api/forms/submit` which expects the payload contract defined in [submission.md](references/submission.md).\nThe generic nature of the payload means that it is important to have some mitigation for potential spam or malicious requests.\n\n## References\n\n- [Modeling](references/modeling.md) — component definitions for the form with parameter tables and slot restrictions\n- [Submission](references/submission.md) — the API route, payload contract and server-side validation\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}