Why skip the Shopify app and the Liquid edits
Search the Shopify App Store for "contact form" and you'll find 50+ apps charging $5–$30/month for what is, mechanically, an HTML <form>tag. The apps add features the built-in template doesn't have — extra fields, file uploads, conditional logic, webhook delivery — but they also add another vendor in your stack, another billing line, another script tag loaded on every page.
The other path is editing the theme's Liquid templates. That works, but it has two real costs: every theme update overwrites your edits unless you're running a custom theme branch, and Liquid debugging is its own learning curve. Most stores end up regretting the edit the first time the theme dev pushes a release.
The third option, which works in 2026 thanks to Online Store 2.0 sections: drop a Custom HTML block on the page in the visual editor and paste your form. No app, no Liquid file changes, no recurring fee. The block survives theme updates because it lives in the page's JSON template, not in the theme's Liquid files. Below: the exact HTML, the page-builder steps, and the gotchas worth knowing.
The form HTML to paste in
This is the entire form. Copy it, swap YOUR_ACCESS_KEYfor your real access key, and you're done. The styling is intentionally minimal — Shopify themes vary, so the form inherits the theme's typography by default; the inline CSS only adds the structural rules a contact form needs.
<form
action="https://splitforms.com/api/submit"
method="POST"
class="splitforms-contact"
>
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY">
<input type="hidden" name="redirect" value="https://yourstore.com/pages/thanks">
<input type="hidden" name="form_source" value="shopify-contact-page">
<!-- Honeypot. Bots fill it; humans never see it. -->
<div aria-hidden="true" style="position:absolute;left:-9999px;height:0;overflow:hidden">
<label>Don't fill this if you're human:
<input type="text" name="website" tabindex="-1" autocomplete="off">
</label>
</div>
<label>
Your name
<input type="text" name="name" required autocomplete="name">
</label>
<label>
Your email
<input type="email" name="email" required autocomplete="email">
</label>
<label>
Order number (optional)
<input type="text" name="order_number" autocomplete="off">
</label>
<label>
How can we help?
<textarea name="message" rows="5" required></textarea>
</label>
<button type="submit">Send message</button>
</form>
<style>
.splitforms-contact {
display: grid;
gap: 14px;
max-width: 32rem;
margin: 24px auto;
font-family: inherit;
}
.splitforms-contact label {
display: grid;
gap: 6px;
font-size: 14px;
line-height: 1.4;
}
.splitforms-contact input,
.splitforms-contact textarea {
padding: 10px 12px;
border: 1px solid rgba(0,0,0,0.18);
border-radius: 8px;
font: inherit;
background: #fff;
}
.splitforms-contact button {
padding: 12px 18px;
border: none;
border-radius: 8px;
background: #111;
color: #fff;
font-weight: 600;
cursor: pointer;
}
.splitforms-contact button:hover { opacity: 0.92; }
</style>A few details worth highlighting. The redirect hidden input sends the visitor to your /pages/thanks Shopify page after a successful submission — build that page first or remove the input. The form_source hidden input tags every submission with where it came from, so if you have multiple forms on the store you can filter them in the dashboard. The order_numberfield is the practical Shopify-specific addition: customers asking about an order will fill it in, and you avoid the "what's your order number" back-and-forth.
Step-by-step in the Shopify page builder
- Sign up at splitforms. Go to /free-contact-form and grab a 32-character access key. Free, no credit card.
- Open the Shopify admin. Online Store → Pages → Add page (or edit an existing /pages/contact).
- Open the page's Theme customizer. Click "Customize page" or the "Customize" button in the page editor. The visual page builder loads.
- Add a Custom HTML / Custom Liquid section. Click "Add section," pick "Custom Liquid" (this is what most Online Store 2.0 themes call it; some themes label it "Custom HTML").
- Paste the form HTML. Drop the form code from the previous section into the Liquid box. Replace
YOUR_ACCESS_KEYwith your real key. - Save and view the live page. The form renders with whatever typography your theme provides. Submit a test message; verify it lands in your inbox within 30 seconds.
That's the whole setup. No theme.liquid edit, no settings_data.json change, no asset upload. The form lives in the page's JSON template, so theme updates won't touch it. If you need the same form on multiple pages (a /pages/contact and a /pages/wholesale-inquiry), drop the same Custom Liquid block on each.
Custom in-page success state (no full-page redirect)
The default behavior with the redirect hidden input does a full page navigation to your thank-you page. Some stores prefer to swap the form for a thank-you message in place — feels more polished, keeps the visitor on the page. This requires a small <script> tag, which is allowed inside Custom Liquid blocks:
<form id="contact-sf" action="https://splitforms.com/api/submit" method="POST">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY">
<input name="name" required>
<input name="email" type="email" required>
<textarea name="message" required></textarea>
<button type="submit">Send</button>
<p id="contact-sf-status" role="status" aria-live="polite"></p>
</form>
<script>
(function () {
var form = document.getElementById('contact-sf');
var status = document.getElementById('contact-sf-status');
if (!form) return;
form.addEventListener('submit', function (e) {
e.preventDefault();
status.textContent = 'Sending…';
fetch(form.action, {
method: 'POST',
body: new FormData(form),
headers: { Accept: 'application/json' },
}).then(function (r) {
if (r.ok) {
form.reset();
status.textContent = 'Thanks — we received your message.';
} else {
status.textContent = 'Something went wrong. Please try again.';
}
});
});
})();
</script>The form keeps a real action URL so it still works without JavaScript (search-engine crawlers, JS-disabled visitors, accessibility tools all see a working form). The script is progressive enhancement — the in-place success state is a nicer experience for the 99% of visitors with JS enabled.
Routing submissions to Slack, Discord, or a CRM
Once the form is live, the splitforms dashboard becomes the place where you decide what happens with each submission beyond the email notification. Webhooks are free on every plan; popular Shopify-store routings:
- Slack channel for the support team. Setup walkthrough.
- Discord channel for community-led brands. Setup walkthrough.
- HubSpot CRM if you're running sales follow-ups. Setup walkthrough.
- Telegram for solo founders who live in their phone. Setup walkthrough.
- Google Sheets for a lightweight CRM-substitute. Setup walkthrough.
- Zapier as a relay to anything else. Setup walkthrough.
Each routing is configured per access key — you don't need to redeploy the form to change destinations. The Custom Liquid block in the Shopify page stays the same; the dashboard does the wiring.
For older Vintage themes: a one-line Liquid include
Vintage themes (anything pre-Online Store 2.0) don't expose Custom Liquid blocks in the page builder. The workaround is a small theme edit — but only one line — to include a Liquid snippet on a page when it's asked for:
{% comment %} templates/page.contact-custom.liquid {% endcomment %}
{% layout 'theme' %}
<div class="page-width">
<h1>{{ page.title }}</h1>
<div class="rte">{{ page.content }}</div>
{% include 'splitforms-contact-form' %}
</div>{% comment %} snippets/splitforms-contact-form.liquid {% endcomment %}
<form action="https://splitforms.com/api/submit" method="POST" class="splitforms-contact">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY">
<label>Your name <input name="name" required></label>
<label>Your email <input name="email" type="email" required></label>
<label>Order number (optional) <input name="order_number"></label>
<label>How can we help? <textarea name="message" required></textarea></label>
<button type="submit">Send</button>
</form>Then in the page's admin settings, set Template = page.contact-custom and the form renders. This is the only Liquid edit anywhere in the flow, and the snippet is small enough to drop onto any theme update without conflict.
splitforms vs Shopify form apps
For comparison, here's how splitforms stacks up against the most popular paid Shopify form apps:
- Powr Form Builder. $10–$60/mo. Visual builder; restricts on free tier; ads on free version.
- Hulk Form Builder. $9–$30/mo. Solid app; expensive for low-volume stores.
- Shopify default contact template. Free but locked — no extra fields, no webhook routing, no spam filtering beyond a basic honeypot.
- splitforms. Free tier covers 1,000/mo with webhooks, file uploads, AI spam filtering. Pro is $5/mo for 5,000 submissions. Pricing details.
The decisive difference for most Shopify stores in 2026 is that splitforms isn't a Shopify app — it's a hosted form backend the store happens to point at. There's no Shopify-specific install step, no app uninstall data-orphan problem, and no recurring app charge on your Shopify bill. The form is just an HTML block.
Get a free key
Sign up at /login for a free splitforms account, or generate an access key without signup at /free-contact-form. Drop the form HTML above into a Shopify Custom Liquid block, paste your access key, save. The contact form is live in under five minutes — no app, no Liquid edits, no recurring fee. The dashboard configuration walkthrough lives in /docs.
FAQ
Why not just use Shopify's built-in contact form template?
Three reasons most stores outgrow it. (1) Limited fields — Shopify's default `contact-us` template is hard-coded to name, email, phone, comment; adding custom fields requires Liquid edits. (2) No spam filtering — Shopify's form has a basic honeypot but no AI classifier or rate limit. (3) No webhooks — submissions go to the store email only, with no way to forward to Slack, Discord, a CRM, or a Zapier flow without an app. The custom HTML approach in this post solves all three.
Will this work on every Shopify theme?
Yes. Every Shopify Online Store 2.0 theme (Dawn, Sense, Crave, Studio, etc.) supports a Custom HTML block in the page builder. The form HTML drops in unchanged. Older Vintage themes need a one-line Liquid include — see the section near the end. The shared element across all themes is that the form posts to an external URL, not to Shopify's `/contact` endpoint.
Does it work on a password-protected (pre-launch) store?
Yes. The form's `action` URL is external (splitforms.com), so it doesn't depend on the store being public. You can build and test the contact form during the store's development phase before flipping the password protection off.
Can I add file uploads (e.g. for custom-product order forms)?
Yes. Add `enctype="multipart/form-data"` to the form and a `<input type="file" name="design_file">`. splitforms accepts files up to 5MB on the free tier. Common Shopify use case: customers uploading a logo or design reference for a custom-product order request.
How do I send Shopify form submissions to a CRM?
Configure a webhook in your splitforms dashboard pointing at the CRM's intake endpoint. For HubSpot, see /blog/send-form-submissions-to-hubspot. For Notion or Airtable as a lightweight CRM, see /blog/send-form-submissions-to-notion and /blog/send-form-submissions-to-airtable. For email tools like Mailchimp or Klaviyo, use Zapier as a relay (see /blog/connect-splitforms-to-zapier).
Will Shopify's checkout / cart code interfere with the form?
No. The form posts to splitforms (an external URL), not to Shopify's internal endpoints, so it doesn't share any code path with the cart, checkout, or customer accounts. It's a self-contained block that lives next to the rest of your page content.
Is the form GDPR-compliant?
The form itself is fine; compliance depends on what you do with the data. splitforms stores submissions in EU-region servers on request, supports DPAs, and lets you delete submissions on demand. Add a consent checkbox to the form and link to your privacy policy. See /blog/gdpr-compliant-form-submissions for the checklist.