Your form. A little more helpful.
Validation, autofill and connected fields. Add ParseAPI to the form you already have.
Get connected
Your form. A little smarter.
Start with an email field. Keep your markup and your styling.
Get a public key.
Set up your website to get a public key for its domain. For a local test, use localhost.
Already have one? Use your domain-restricted public key from Keys.
Add one script.
Paste this inside your existing form. Replace parse_public_YOUR_KEY with your public key and include the script once.
<script src="https://cdn.parseapi.com/v1/form.js"
data-key="parse_public_YOUR_KEY" defer></script>
<label for="newsletter-email">Email</label>
<input id="newsletter-email" name="email" type="email"
autocomplete="email" data-parse="email">Try a field.
Enter an email and leave the field to see feedback. Style the .parse-hint helper to match your site.
Add names, phone numbers, address suggestions and autofill with the Form examples. Your existing form keeps its submission behavior.
Make it work with your app.
Send values to your backend, add visitor context, or use your own components and validation.
Add context to your form
Paste these fields inside your form. Keep the outputs you need and use unique input ids.
<!-- Visit details run once after someone starts filling out this form. -->
<!-- Deep data is included with paid plans. -->
<input id="visit-ip" type="hidden" data-parse="ip" data-parse-deep="true">
<input id="visit-useragent" type="hidden" data-parse="useragent" data-parse-deep="true">
<input type="hidden" name="ip" data-parse-fill="ip" data-parse-from="visit-ip">
<input type="hidden" name="ip_country" data-parse-fill="country" data-parse-from="visit-ip">
<input type="hidden" name="ip_asn" data-parse-fill="asn" data-parse-from="visit-ip">
<input type="hidden" name="ip_state" data-parse-fill="state" data-parse-from="visit-ip">
<input type="hidden" name="ip_city" data-parse-fill="city" data-parse-from="visit-ip">
<input type="hidden" name="ip_vpn" data-parse-fill="vpn" data-parse-from="visit-ip">
<input type="hidden" name="ip_tor" data-parse-fill="tor" data-parse-from="visit-ip">
<input type="hidden" name="ip_relay" data-parse-fill="relay" data-parse-from="visit-ip">
<input type="hidden" name="ip_datacenter" data-parse-fill="datacenter" data-parse-from="visit-ip">
<input type="hidden" name="device" data-parse-fill="device" data-parse-from="visit-useragent">
<input type="hidden" name="os" data-parse-fill="os" data-parse-from="visit-useragent">
<input type="hidden" name="browser" data-parse-fill="browser" data-parse-from="visit-useragent">
<input type="hidden" name="bot" data-parse-fill="bot" data-parse-from="visit-useragent">
<input type="hidden" name="bot_name" data-parse-fill="bot_details.name" data-parse-from="visit-useragent">Use context on your server
For server decisions, look up context from the incoming request. Download the Node.js helper, set your secret PARSEAPI_KEY on the server, then call:
import { getFormContext } from './form-context-server.node.mjs';
const context = await getFormContext({ visitorIp, userAgent });Get visitorIp from your framework's trusted proxy configuration, never a submitted hidden field or an unchecked forwarded header. Pass the request's User-Agent as userAgent; it is still client-controlled. Missing answers are null. Your backend decides what needs review.
Choose what goes to your backend
Your input's name is what your backend receives. data-parse-fill chooses the returned value; data-parse-from connects it to a source input. Use hidden inputs for extra details or visible, editable fields. Dotted keys read nested answers.
<!-- Include form.js once, then add your own fields. -->
<input id="billing-country" name="country"
data-parse="/country/{code}" placeholder="US">
<input type="hidden" name="billing_currency"
data-parse-from="billing-country" data-parse-fill="currency">
<input type="hidden" name="calling_code"
data-parse-from="billing-country" data-parse-fill="calling_code">Without data-parse-from, an output uses the preceding marked input. Keep input ids unique. Missing answers stay empty; browser-filled fields remain client-controlled.
Use your design and validation rules
Form adds no stylesheet or inline styles. Style the default helpers with .parse-hint, .parse-hint-suggestion and .parse-address-suggestions. Use data-parse-status and data-parse-reason for field states.
For your own components, set data-parse-ui="none". To own validation too, set data-parse-validation="manual". Native HTML rules still apply. Set these options before attachment.
<!-- Include form.js once. Your markup, CSS, and messages. -->
<input id="contact-email" type="email" name="email"
data-parse="email" data-parse-ui="none"
data-parse-validation="manual" aria-describedby="email-help">
<p id="email-help" role="status"></p>
<button id="email-correction" type="button" hidden></button>
<script>
const email = document.getElementById("contact-email");
const help = document.getElementById("email-help");
const correction = document.getElementById("email-correction");
const copy = {
invalid: "Check the email format.",
domain_invalid: "Check the domain after @.",
disposable: "Use an email you plan to keep.",
unavailable: "Email check unavailable. You can still send the form."
};
email.addEventListener("parse", ({ detail }) => {
const invalid = ["invalid", "disposable"].includes(detail.status);
const message = copy[detail.reason] ?? detail.message ?? "";
help.textContent = message;
email.setCustomValidity(invalid ? message : "");
email.setAttribute("aria-invalid", String(invalid));
correction.hidden = !detail.suggestions.length;
correction.textContent = detail.suggestions[0]?.label ?? "";
correction.onclick = () => detail.select?.(0);
});
</script>The bubbling parse event returns status, value, answer, reason, message, suggestions and select(index). Your UI owns focus, keyboard interaction and accessibility. Stale selections are ignored after edits.
Use dynamic or framework forms
Include the script once. Call parseform.scan() after mounting new inputs; existing fields stay attached once. With controlled inputs, keep your framework state in sync with input and change events, or handle the parse event in your own components.
Keep unique input ids across forms. Editing a source clears its old generated answers, and stale requests cannot overwrite newer input. Manual corrections remain yours.