GrowSitedevelopers

Forms step by step

Instances, site binding, submission and error handling.

A working form in a Loom theme

A form consists of a GrowSite instance, field configuration and a page element referencing its instance_id. The theme supplies presentation; the instance belongs to one website. Never ship a customer's form ID inside a reusable theme package.

The example package includes a contact page and sections/contact.html with a heading, description and gs-form. Enter the instance ID in form_id, or add a separate canvas form. The current importer does not create form instances from Loom schema.

1. Prepare the site and instance

  1. Enable contact-form for the site/workspace.
  2. Open the contact page and add a form element to the section canvas.
  3. Choose an existing instance or create one in the element settings.
  4. Name it, configure fields, required answers and a success message.
  5. Save the form. The builder assigns its returned ID to the element.
  6. Save the page and test submission on the public view of a test site.

Submission is disabled in the editor. A submit button doing nothing in edit mode does not indicate a broken endpoint. The instance must be active and belong to the same site.

2. Instance configuration

This JSON configures an instance; it is not templates/contact.json or Loom schema. Reproduce it in the form editor. Application integrators may send it to authenticated POST /api/v1/sites/{site}/plugin-instances as a user authorized to manage that site.

json
{
  "key":"contact-form",
  "name":"Contact form",
  "status":"active",
  "page_id":null,
  "configuration":{
    "fields":[
      {"key":"name","label":"Name","type":"text","required":true},
      {"key":"email","label":"Email","type":"email","required":true},
      {"key":"topic","label":"Topic","type":"select","required":true,"options":["Quote","Support"]},
      {"key":"message","label":"Message","type":"textarea","required":true}
    ],
    "success_message":"Thank you. Your message has been received."
  }
}

page_id may identify one of this site's pages or be null. Instance names allow 120 characters and success messages 300. See inputs for field limits. Update an instance through PATCH /api/v1/sites/{site}/plugin-instances/{instance}.

3. Connect the canvas element

This is the shape of a node in content.canvas.nodes. Replace the example ID with the saved instance ID; preferably let the builder select and persist it.

json
{
  "id":"contact-node",
  "type":"form",
  "name":"Contact",
  "instance_id":"01ARZ3NDEKTSV4RRFFQ69G5FAV",
  "style":{"padding":24,"radius":16}
}

content.canvas also contains version:1, slots, fields and optional elements. These are page data, not Loom block settings. The renderer turns a form node into a PluginRenderer instance of contact-form.

For a native Loom section, shared SectionCanvas renders canvas nodes after section content. Do not add another gs-canvas just to display the form: that can render nodes twice. Use gs-form from step 5 to embed the form inside a section.

4. Submission and errors

The shared component posts to /api/v1/render/sites/{slug}/contact. PluginProvider supplies the API host and endpoint; do not hardcode the production host in the theme. Request shape:

json
{
  "instance_id":"01ARZ3NDEKTSV4RRFFQ69G5FAV",
  "values":{"name":"Anna","email":"anna@example.com","topic":"Quote","message":"Please contact me."},
  "website":""
}

website is a hidden protection field and must stay empty. Only keys declared by the instance belong in values. The API checks instance ownership/status, site availability, plugin access and answer types. The endpoint is rate limited.

On success, the component displays the configured or localized success message and resets fields. On failure, it retains values and displays an error. The button is disabled while busy and status uses aria-live="polite". Do not attach a second submission handler.

Submission requires a published site. Local draft rendering depends on sites.render_drafts; this is not production behavior. Recording a submission does not itself integrate with an external newsletter service.

5. Embed directly in Loom

html
<gs-form instance-id="[[ section.settings.form_id ]]" submit-label="Send"></gs-form>

Add a text schema setting with ID form_id, label “Form ID” and an empty default. After creating the form in the panel, enter its real ID into this section setting. Use the same setting name in blocks so preview can load the instance. Assign the form to this page or the whole site. Do not simultaneously add the same form as a canvas node.

No TSX adapter is required. The element uses the shared component: a disabled plugin, empty ID or unavailable instance produces no output. Optional submit-label changes the button label; fields and success messages come from the instance. Never distribute a package containing a customer's form ID.

6. Test the complete flow

Check missing instances, disabled plugins, an instance owned by another site, an empty required email, an invalid option, successful submission, a network error and retry. Responses should appear in the correct site's submissions. Confirm a double click does not send concurrent requests and that reloading the editor preserves the selected instance.

Contact page files

sections/contact.html

html
<section class="launch-contact">
  <h1 data-text-field="[[ section.settings_path ]].title" gs-text="section.settings.title"></h1>
  <p data-text-field="[[ section.settings_path ]].text" gs-text="section.settings.text"></p>
<gs-form instance-id="[[ section.settings.form_id ]]"></gs-form>
</section>

templates/contact.json

json
{"title":"Contact","sections":{"contact":{"type":"contact"}},"order":["contact"]}
Download Markdown copy
Results · 22
GS Loom UI components

Shared components, variants and theme styling.

Font catalog

Available fonts, previews and font declarations in GS Loom.

Menus and navigation

Create menus, connect links and style navigation.

Blog and comments

Blog and comments

SEO and plugin contracts

SEO and plugin contracts

Build your first theme

A complete package, installation and your first edit.

GS Loom Playground

Test themes locally without an API, database or upload.

Architecture and files

How author files become a customer website.

Schema and settings

Control types, default values and stable identifiers.

Blocks, targeting and order

Definitions, instances, nesting and static blocks.

Preview editing

Connect text, cards, images and icons to the builder.

Text and headings

From schema to text editing, typography and persistence.

Buttons and links

Labels, destinations, colors, dimensions and accessible states.

Inputs and form fields

Field types, labels, options, limits and validation.

Forms step by step

Instances, site binding, submission and error handling.

Cards, icons, images and badges

Container appearance, icon sizes and media editing.

Dynamic sources

Site data, page context and fallback values.

Layouts and shared settings

Section groups, global configuration and page templates.

Languages and translations

Starter languages, customer-added languages and AI translations.

Snippets, CSS and assets

Reusable markup, responsive styling and safe URLs.

Testing, import and updates

Validate a package and release it while preserving customer data.

Capabilities and limits

The supported GS Loom contract and working with AI.