Check an email as someone types

Add email validation and optional typo corrections to an existing HTML input with form.js.

JavaScript (browser)Last tested Download and run ↓
On this page

Working example

HTML + form.js

Run the example

Download HTML

check-email-before-submit.html

Replace YOUR_PUBLIC_API_KEY in the downloaded file with your public key. Add localhost to that key's allowed domains. In a terminal in the download folder, run:

Terminal · Python 3
python3 -m http.server 8000

Open localhost:8000/check-email-before-submit.html. When publishing, add your website's hostname to the key. Keep secret keys on your server.

Need a key? Set up this example

Mark your email field

Add email validation to an existing HTML field with one script include and data-parse="email". Keep the field's label, name, and native HTML rules.

HTML
<script src="https://cdn.parseapi.com/v1/form.js" data-key="YOUR_PUBLIC_API_KEY" data-parse-version="2.0.0" defer></script>
<div class="field">
  <label for="email">Email</label>
  <input id="email" type="email" name="email" data-parse="email" autocomplete="email" placeholder="you@example.com" required>
</div>

The script checks a complete-looking address after a typing pause or when the field loses focus. For a likely typo, such as hello@gmail.con, it offers a spelling correction beside the field. The person chooses whether to accept it.

Keep submission yours

The default preset marks an invalid format, an explicitly invalid mail domain, or a disposable address as invalid through the browser's native form validation. An unknown domain is not a failed check. Your required and type="email" rules still apply.

The script never waits for a pending lookup before submission. If the request is unavailable, it leaves the field unchecked. Your server must validate the submitted value and apply your signup rules.

Change the address after a check and confirm the old feedback clears. Format and domain checks help with entry; use server email verification when your workflow needs a delivery check. Ownership still needs your own confirmation flow.

For your own validation policy, data-parse-validation="manual" leaves custom validity to your code. Build directly with the Email API when you need a custom integration.

Complete example

One HTML file with your fields, styles, and the form.js include. Replace YOUR_PUBLIC_API_KEY with your public key, then run it on a domain you have added to that key.

View and copy the complete source
check-email-before-submit.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Check an email as someone types</title>
  <style>
    :root { color-scheme: light dark; --bg: #fff; --ink: #152029; --muted: #5e6b75; --line: #d9e2e7; --field: #f6f9fa; --accent: #007c91; --error: #b42335; }
    @media (prefers-color-scheme: dark) {
      :root:not([data-theme="light"]) { --bg: #0b1015; --ink: #eff7fa; --muted: #94a6b2; --line: #2c3942; --field: #121b23; --accent: #67e8f9; --error: #fda4af; }
    }
    :root[data-theme="dark"] { color-scheme: dark; --bg: #0b1015; --ink: #eff7fa; --muted: #94a6b2; --line: #2c3942; --field: #121b23; --accent: #67e8f9; --error: #fda4af; }
    :root[data-theme="light"] { color-scheme: light; }
    * { box-sizing: border-box; }
    [hidden] { display: none !important; }
    body { margin: 0; padding: 24px; background: var(--bg); color: var(--ink); font: 15px/1.5 system-ui, sans-serif; }
    main { max-width: 540px; margin: 0 auto; }
    label { display: block; margin-bottom: 8px; font-size: 14px; font-weight: 600; }
    input { display: block; width: 100%; min-width: 0; padding: 13px 14px; border: 1px solid var(--line); border-radius: 8px; background: var(--field); color: var(--ink); font: inherit; }
    input::placeholder { color: var(--muted); }
    :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
    /* Your form owns these styles. form.js adds semantic markup and states. */
    input[data-parse-status="invalid"], input[data-parse-status="disposable"] { border-color: var(--error); }
    .parse-hint { margin: 6px 0 0; color: var(--muted); font-size: .875em; line-height: 1.4; overflow-wrap: anywhere; }
    .parse-hint[data-parse-status="invalid"], .parse-hint[data-parse-status="disposable"] { color: var(--error); }
    .parse-hint-suggestion { padding: 0; border: 0; background: none; color: inherit; font: inherit; font-weight: 600; text-decoration: underline; text-underline-offset: 2px; cursor: pointer; }
    .parse-hint-suggestion:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
    @media (max-width: 380px) { body { padding: 18px; } }
  </style>
</head>
<body>
  <main>
    <!-- example:form -->
    <script src="https://cdn.parseapi.com/v1/form.js" data-key="YOUR_PUBLIC_API_KEY" data-parse-version="2.0.0" defer></script>
    <div class="field">
      <label for="email">Email</label>
      <input id="email" type="email" name="email" data-parse="email" autocomplete="email" placeholder="you@example.com" required>
    </div>
    <!-- /example:form -->
  </main>
</body>
</html>

Need a key? Set up this browser example. For every field and parameter, see Email API reference.

Build something else

All tutorials →