# Collect data with forms

> A Webflow form that saves each submission as a metaobject entry in Shopify, with file uploads, drafts to moderate, and automations in Shopify Flow.

Status: Ready (Smootify 2.0)  
Plan: Server plan  
Add-on: Metaobject creator  
1.x page: https://docs.smootify.io/docs/metaobjects/creator  
URL: https://www.smootify.io/docs/features/metaobject-creator  
Updated: 2026-10-10

With `metaobject-creator`, a Webflow Form Block saves each submission as a metaobject entry in Shopify, instead of in Webflow’s form submissions. The data sits in the same place as your products, orders and customers. You can show it again on the site, and Shopify Flow can react to it. Use it for warranty registrations, reseller applications, event sign-ups or reviews.

> [!NOTE]
> The metaobject creator needs the **Server** plan and **Metaobject creator** turned on in the Smootify dashboard. It runs on the site's licensed domain. On your `.webflow.io` address it runs once the site is paid, not during a trial.

## A Webflow form or the metaobject creator

The same Form Block, sent to two different places:

| | Webflow form | Metaobject creator |
|---|---|---|
| Where the data goes | Webflow's form submissions | Shopify, as metaobject entries |
| Shown again on the site | no | yes, with [Show metaobjects](https://www.smootify.io/docs/features/metaobjects) |
| Automations | Webflow's form emails | Shopify Flow |

## 1. Create the definition in Shopify

Under **Settings > Custom data > Metaobjects**, add a definition with the fields you want to collect. Turn on:

- **Active-draft status**, to save entries as drafts with `data-draft` and approve them before they show (step 4). Without it, entries are saved without a status, and `data-draft` has no effect;
- **Storefronts** access, if you want to show the entries on the site. Saving works without it.

Write down the definition’s **type**, such as `warranty_registration`, and the **key** of each field, such as `serial_number` for “Serial number”.

## 2. Build the form

Add a Custom element with the tag `metaobject-creator` and give it the custom attribute `data-type` with the type. Put a Webflow Form Block inside it. Without a form, the element is removed.

```html
<metaobject-creator data-type="warranty_registration" data-draft="true">
  <div class="w-form">
    <form>
      <input name="serial_number">
      <input name="purchase_date">
      <label class="w-checkbox"><input type="checkbox" name="accepts_reminders" class="w-checkbox-input"><span class="w-form-label">Remind me before the warranty ends</span></label>
      <file-input name="receipt"></file-input>
      <input type="submit" value="Register" data-wait="Sending…">
    </form>
    <div class="w-form-done"><div>Thank you. Your product is registered.</div></div>
    <div class="w-form-fail"><div>Something went wrong</div></div>
  </div>
</metaobject-creator>
```

**Field names are the keys.** Each field’s **Name**, in its element settings, is the key of a field of the definition, such as `serial_number`, or the same words with spaces, such as `Serial number`: Smootify turns the Name into the key. Webflow renames fields when you copy a form, so check every Name after pasting. From the definition to the entry, for three of the fields above:

1. **Settings > Custom data > Metaobjects**: the definition Warranty registration, type `warranty_registration`, with the fields Serial number (key `serial_number`, single line text), Accepts reminders (key `accepts_reminders`, true or false) and Receipt (key `receipt`, file). **Active-draft status** on, to save entries as drafts with `data-draft`. **Storefronts** access only to show the entries on the site.
2. In Webflow, inside `metaobject-creator`, each field's **Name** setting is a key: a Text field named `serial_number` sends what the visitor types; a Checkbox named `accepts_reminders` sends `true` when checked, and `false` when unchecked; a `file-input` with `name="receipt"` sends the file, which goes to the store's Shopify Files. Empty fields are not sent, and neither are fields whose Name starts with `_`: use those for fields that only help the visitor, such as "repeat your email".
3. **Content > Metaobjects > Warranty registration**: the new entry, with its values. With `data-draft="true"` on `metaobject-creator`, it is a draft until you make it active.

**What each field type takes.** Smootify’s server turns what the form sends into the format of each field of the definition, so most fields need only the plain Webflow element. What the visitor writes, and what Shopify saves:

| Field type in Shopify | In the form | The visitor writes → Shopify saves |
|---|---|---|
| Single line text, Multi-line text | a Text field or a Text area | `Linen` → Linen |
| Rich text | a Text area: each line is a paragraph | two lines → two paragraphs |
| Integer, Decimal | a Text field | `4,5` → 4.5 |
| Rating | a Text field, a Select field or Radio buttons, with a number on the field's scale | `4` → 4 out of 5 |
| Money | a Text field, in your store's currency | `9,90` → 9.90 |
| Dimension, volume, weight | a Text field for the number, and a Select field with `data-unit-for` set to that field's Name, whose options are the units; or one Text field with the number and its unit | `2,5` and `cm` → 2.5 cm |
| True or false | one Checkbox with that Name | checked → true, not checked → false |
| A list of any type | several Checkboxes with the same Name, each with the custom attribute `value`; a Select field with the custom attribute `multiple`; or a Text field with the values separated by commas | Red and Green ticked → Red, Green |
| Date | a Text field, or a Custom element with the tag `input` and `type="date"` | `2026-10-02` → 2 October 2026 |
| Date and time | a Custom element with the tag `input` and `type="datetime-local"` | a date and a time → that date and time |
| URL | a Text field | `smootify.io` → https://smootify.io |
| Product, variant, collection, page or metaobject reference | a Text field, or a Custom element with the tag `input` and `type="hidden"`, holding the id | `9304502009994` → that product |
| Customer reference | `customer-field` on `metaobject-creator` | the logged-in customer |
| File, list of files | `file-input`, with `data-multiple` for a list | the uploaded files, in Shopify Files |

A value already in Shopify’s own format passes as it is. For a measure, the Select field with `data-unit-for` is not sent as a field of its own, so its Name does not matter. A number without a unit is refused by Shopify, with its reason in the Error message.

**How many of each product.** Next to each product checkbox, in the same card, a Number field with the custom attribute `data-quantity-for` set to the checkboxes’ Name (`data-quantity-for="products"`) says how many of that product the entry asks for, such as 6 glasses on a gift list. Give the Number fields the Name of a **JSON** field of the definition, such as `quantities`: Smootify sends, for each checked product, its quantity, at least 1. The products of a gift list page then show how many are gifted: see [Product cards from a field](https://www.smootify.io/docs/features/metaobjects#5-product-cards-from-a-field).

**The product and the variant on the page.** A form inside a `smootify-product`, such as a back-in-stock alert, can save which variant the visitor was looking at: a hidden field with the custom attribute `variant="id"` takes the current variant’s ID and follows every change of option. Give it the Name of a variant reference field of the definition, such as `variant`.

```html
<input type="hidden" name="variant" variant="id">
```

**Required fields inside components.** Turn on **Required** in a field’s settings. On a field inside a Webflow component, where Webflow cannot keep it, give the field the custom attribute `data-required` instead; `False` turns it off. This works in every form of the page, a Smootify form or a plain Webflow form such as a contact form, and a field hidden by a condition does not count.

**The Form Block’s messages.** The Success message shows once the entry is saved, and the form hides. When Shopify refuses the entry, the Error message shows Shopify’s reason in its text, such as a field that does not exist or a required field left empty. The Form Button’s **Waiting text** shows while it saves.

**Fields that are not saved.** Every filled field is sent, and a field whose key is not in the definition makes Shopify refuse the whole entry. For a field the entry does not need, such as a checkbox to accept your terms, start its Name with `_`, such as `_accepted`: Smootify does not send it. Mark it **Required** to stop the form until it is checked.

## 3. Upload files

For a file field, add a Custom element with the tag `file-input` inside the Form, with the custom attribute `name` set to the field’s key. The files go to your store’s Shopify Files, and the entry gets them.

- For a field that holds a list of files, add `data-multiple` with the most files allowed, such as `3`. Without a number, the limit is 5.
- You design the upload area with the same parts and limits as the file uploader, such as a drop zone, a list of files and `max-file-size`: see [File uploads](https://www.smootify.io/docs/features/file-uploads).
- While a file is still uploading, the form cannot be sent.

Shopify Files has its own limits on size and file types. For larger files, upload them with another service and give the definition a **URL** field instead of a file field: the form sends the file’s address in a field with that field’s Name. A file field does not take a file stored outside Shopify.

## 4. Moderate with drafts

With `data-draft="true"` on `metaobject-creator`, each entry is saved as a draft. It stays off the site until you make it active in Shopify. Use it for anything visitors write that others will read, such as reviews. Without it, entries are active as soon as they are sent.

## 5. Link the entry to the customer

To know which customer sent each entry, such as a warranty registration or a reseller application:

1. In the definition, add a field of type **Customer** (a customer reference), with a key such as `customer`.
2. On `metaobject-creator`, add the custom attribute `customer-field` with that key: `customer-field="customer"`.

```html
<metaobject-creator data-type="warranty_registration" customer-field="customer">
  <div class="w-form">
    <form><input name="serial_number"><input type="submit" value="Register" data-wait="Please wait…"></form>
    <div class="w-form-done"><div>Thank you. Your product is registered.</div></div>
    <div class="w-form-fail"><div>Something went wrong.</div></div>
  </div>
</metaobject-creator>
```

- **No field to add.** You do not add the customer to the form. Smootify’s server finds out who is logged in and writes the customer into that field. A value sent with the form under that key is ignored, so nobody can send an entry in another customer’s name.
- **Only for logged-in customers.** Until the customer logs in, the form is hidden and cannot be filled. Once they are logged in, `metaobject-creator` gets the class `is-customer` and the form shows. Put a login link next to it for the others: see [Customer accounts](https://www.smootify.io/docs/build/account).
- **The session must be valid.** When the login has expired, the form shows “Log in to send this form” in its Error message.

**Only for some customers.** To offer the form only to some customers, such as resellers you approved, put it inside an element with `customer-tagged="reseller"`, or with `if-customer-metafield` on a metafield of the customer. A Shopify Flow workflow can then add or remove the tag. These conditions only show or hide the form on the page: see [customer conditions](https://www.smootify.io/docs/reference/conditions/customer).

**The customer’s entries in the account.** With `customer-field`, Smootify’s server also keeps a list of the customer’s entries: each new entry goes first into the customer’s metafield `smootify.<type>`, such as `smootify.gift_list`. The metafield is created the first time, and keeps the 128 newest entries. An account page lists them with `smootify-metaobjects`: see [The logged-in customer’s entries](https://www.smootify.io/docs/features/metaobjects#the-logged-in-customers-entries).

## 6. Share the new entry

Once the entry is saved, `metaobject-creator` gets the class `is-created` and the custom attribute `data-handle` with the entry’s handle. Inside the Form Block’s Success message:

- a Link block with the custom attribute `data-handle-param` links to the entry. Point the link to the page that shows one entry, such as `/gift-list`, and give `data-handle-param` the name of that page’s parameter, such as `l`: Smootify writes `/gift-list?l=<handle>` into it. That page shows the entry with `smootify-metaobject` and the same `data-handle-param`: see [The entry named in the page link](https://www.smootify.io/docs/features/metaobjects#3-one-entry);
- a Custom element with the tag `button` and `data-action="share"` shares the same link: the device’s share sheet on phones, otherwise the link is copied. Add `data-copy` to always copy it, and `data-title` to name the share. For two seconds after a click, the button has the class `is-shared` or `is-copied`: show a “Link copied” text with that combo class.

```html
<metaobject-creator data-type="gift_list" customer-field="customer">
  <div class="w-form">
    <form>…</form>
    <div class="w-form-done">
      <div>Your list is ready</div>
      <a href="/gift-list" data-handle-param="l">Open your list</a>
      <button type="button" data-action="share" data-title="My gift list">Copy the link</button>
    </div>
  </div>
</metaobject-creator>
```

## 7. Let the customer edit or delete the entry

`metaobject-editor` opens an entry the logged-in customer created and lets them change it or delete it, such as their gift list or a warranty registration. It takes the same attributes as the form that created the entry: `data-type`, `customer-field` and, for the entry named in the page link, `data-handle-param`.

```html
<metaobject-editor data-type="gift_list" customer-field="customer" data-handle-param="l">
  <div class="w-form">
    <form>
      <input name="title">
      <textarea name="message"></textarea>
      <input type="submit" value="Save" data-wait="Saving…">
    </form>
    <div class="w-form-done"><div>Saved.</div></div>
    <div class="w-form-fail"><div>Something went wrong.</div></div>
  </div>
  <button type="button" data-action="delete" data-confirm="Delete this list?" data-redirect="/account/gift-lists">Delete the list</button>
</metaobject-editor>
```

- **The form opens filled in** with the entry: texts, checkboxes, radios, selects, quantities and `file-input`. Saving changes the entry.
- **Only its owner sees it.** Smootify’s server checks that the logged-in customer is the one in the `customer-field` of the entry. For the owner the element gets the class `is-owner`; for anyone else it stays hidden.
- **Delete.** A Custom element with the tag `button` and `data-action="delete"` deletes the entry and takes it out of the customer’s entries. `data-confirm` asks first with that text, and `data-redirect` opens that page afterwards. Without a redirect, the element gets the class `is-deleted`.
- It needs the Server plan and Metaobject creator, like the form that creates the entries.

## 8. React with Shopify Flow

The free **Shopify Flow** app can start an automation each time an entry is created: send an email, tag a customer, update a metafield, call another service. Install it in Shopify, then create a workflow that starts when a metaobject entry is created. With `customer-field`, the entry already holds the customer, so the workflow can tag them or update their metafields.

Ready-made workflows are in the [Flow library](https://www.smootify.io/flows), each with the definition to create and a file to import: [review moderation](https://www.smootify.io/flows/review-moderation) and [review requests](https://www.smootify.io/flows/review-request), and alerts for [back in stock](https://www.smootify.io/flows/back-in-stock), a [waitlist](https://www.smootify.io/flows/waitlist) and a [price drop](https://www.smootify.io/flows/price-drop).

> [!TIP]
> These forms often collect personal data. Tell visitors what you collect and why, and decide which of your Shopify staff can see the entries.

## Before you publish

> [!STUCK]
> - **Nothing is saved, and the Error message names a field.** A field's Name is not the key in Shopify. Webflow renames fields when you copy a form: fix each Name.
> - **Shopify refuses a measure.** The number has no unit: add a Select field with `data-unit-for`, or let the visitor write the unit.
> - **The form disappears on the live site.** The Server plan or Metaobject creator is missing, or the page is not on the licensed domain: Smootify then removes the element.
> - **Entries are saved but do not show on the site.** The definition has no Storefronts access, or the entries are drafts.
> - **Files are missing from the entry.** The `name` of `file-input` is not the key of the file field.
> - **The share link goes to the page without the entry.** The link's `data-handle-param` is not the parameter that the entry page's `smootify-metaobject` reads, or the link is outside the Success message.
> - **The form never shows.** It has `customer-field` and the visitor is not logged in, or Customer Accounts is not set up on the site: see [Customer accounts](https://www.smootify.io/docs/build/account).
> - **"This form cannot create entries of this metaobject type".** `data-type` is a definition the form cannot write: Shopify's own (`shopify--…`), an app's (`$app:…`), or one of Smootify's, such as the swatch options or the Magic Box. Create a definition of your own.
