formtoDashboard

Shopify

Where the code goes

In the theme editor add a Custom Liquid section (or edit a page and switch to HTML) and paste the generated HTML. The form works alongside Shopify's own contact form.

The origin to allow

Allow both your custom domain and your myshopify.com domain as allowed origins.

Thank-you handling

Set the form's success redirect to a Shopify page such as /pages/thanks, or keep the in-place message.

File uploads

A file field uploads to formto, so customers can attach photos to a request without a Shopify app.

The code

Generated from the form’s fields (plain HTML with a small script). Replace YOUR_FORM_ID with your form’s public id, then restyle it; keep the field names and the hidden _gotcha honeypot.

<form id="formto-form" action="https://api.formto.io/f/YOUR_FORM_ID" method="post" enctype="application/x-www-form-urlencoded">
  <h2>Contact</h2>
  <p>
    <label for="formto-name">Name</label>
    <input type="text" id="formto-name" name="name" required>
  </p>
  <p>
    <label for="formto-email">Email</label>
    <input type="email" id="formto-email" name="email" autocomplete="email" required>
  </p>
  <p>
    <label for="formto-message">Message</label>
    <textarea id="formto-message" name="message" rows="5" required></textarea>
  </p>
  <!-- Spam trap: people never see or fill this field. -->
  <input type="text" name="_gotcha" tabindex="-1" autocomplete="off" aria-hidden="true" style="position:absolute;left:-10000px">
  <button type="submit">Send</button>
  <p data-formto-status role="status" aria-live="polite"></p>
</form>
<script>
  // Progressive enhancement: without JavaScript the form above posts natively.
  ;(function () {
    var form = document.getElementById('formto-form')
    if (!form || !window.fetch || !window.FormData) return
    form.addEventListener('submit', function (event) {
      event.preventDefault()
      var status = form.querySelector('[data-formto-status]')
      var button = form.querySelector('button[type=submit]')
      button.disabled = true
      fetch(form.action, { method: 'POST', body: new FormData(form), headers: { Accept: 'application/json' } })
        .then(function (res) {
          return res.json().then(function (body) {
            if (!res.ok || !body.success) throw new Error(body.error || 'Something went wrong. Please try again.')
            form.reset()
            status.textContent = 'Thanks, we got it.'
          })
        })
        .catch(function (err) {
          status.textContent = err.message || 'Could not send. Please try again.'
        })
        .finally(function () {
          button.disabled = false
        })
    })
  })()
</script>

Other platforms: Plain HTML, React, Next.js, Astro, WordPress, Squarespace, Webflow, Wix, Framer.