> ## Documentation Index
> Fetch the complete documentation index at: https://docs.liteshop.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Forms

> Declare a Form in liteshop.json and send Visitors' messages to the dashboard with the Liteshop SDK.

A Form lets Visitors send a message to the Site Owner. Each submission appears
in the **Messages** page of the dashboard. Visitors can't read submissions, and
the Site's code never receives the Owner's credentials.

## Declare the Form

Add the Form to `capabilities.forms` in `liteshop.json`:

```json theme={null}
{
  "capabilities": {
    "forms": [
      {
        "id": "contact",
        "name": { "en": "Contact" },
        "fields": [
          {
            "id": "email",
            "type": "email",
            "label": { "en": "Email" },
            "required": true,
            "maxLength": 200
          },
          {
            "id": "message",
            "type": "textarea",
            "label": { "en": "Message" },
            "required": true,
            "maxLength": 2000
          }
        ]
      }
    ]
  }
}
```

| Field type | Options |
| - | - |
| `text`, `email`, `tel`, `textarea` | `minLength`, `maxLength` |
| `number` | `minimum`, `maximum` |
| `checkbox` | None |
| `select` | `options`, required: a list of `{ "value", "label" }` entries |

Keep Form and field IDs unchanged after the Site receives messages. A
Deployment can't remove an ID that stored messages refer to.

## Submit the Form

1. Load the SDK on the page, before your script. A Site built with a framework
   imports it instead, with `import { liteshop } from "@liteshop.ai/sdk"`.

   ```html theme={null}
   <script src="/_liteshop/sdk/v1.js"></script>
   <form id="contact-form">
     <label>Email <input name="email" type="email" required /></label>
     <label>Message <textarea name="message" required></textarea></label>
     <div class="form-trap" aria-hidden="true">
       <label>Website <input name="_liteshop_honeypot" tabindex="-1" autocomplete="off" /></label>
     </div>
     <button type="submit">Send</button>
   </form>
   <script src="contact.js"></script>
   ```

2. In your script, stop the browser's own submit and send the values with
   `liteshop.forms.submit`:

   ```js theme={null}
   const form = document.querySelector("#contact-form");

   form.addEventListener("submit", async (event) => {
     event.preventDefault();
     const values = Object.fromEntries(new FormData(form));

     try {
       await liteshop.forms.submit("contact", values);
       form.reset();
       // Show a success message.
     } catch (error) {
       // Show error.message. error.field names the rejected field, if any.
     }
   });
   ```

Follow these rules, which validation checks:

* Pass the Form ID as a literal string, such as `"contact"`. Every declared Form
  needs a matching call.
* Don't add `method="post"` or an `action` to the form. Liteshop doesn't accept
  forms posted to the server.
* Load the SDK before the script that calls it.

Liteshop checks types strictly and doesn't convert strings. Convert `number`
inputs to numbers and `checkbox` inputs to booleans before you send them.

## Protect the Form from spam

The `_liteshop_honeypot` input catches automated form fillers. Hide it with CSS
rather than `type="hidden"`, so that bots still fill it in:

```css theme={null}
.form-trap {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}
```

The SDK removes the honeypot from the values and doesn't store it. Liteshop
also limits each Visitor to five submissions per Form every 10 minutes, and each
Site to 100 submissions every hour. Above the limit, the SDK rejects with
`RATE_LIMITED`.

## Language and retries

The SDK sends the page language from `<html lang>`. Liteshop uses it if the
Site declares that language, and otherwise uses the Site's default language.

Each submission has an identifier, and the SDK retries one network failure with
the same identifier, so a retry never creates a duplicate message.

## Manage messages

In the dashboard, the **Messages** page lists submissions. The Owner can mark
them as read or unread, correct them, and delete them.

> [!NOTE]
>
> Previews can't send forms. To test a submission, publish the Site and use its
> live address, or use the [local dev plugin](https://docs.liteshop.me/develop/local-development.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.