How to Create a Custom Shopify Form Without Apps

Build a Custom Shopify Form

Save Money on Shopify: Build Your Own Custom Forms in Minutes

Are you still paying monthly subscriptions just to have a custom contact form on your Shopify store? ๐Ÿ›‘ Stop right there.

With the power of Shopify Online Store 2.0, you can build robust, fully custom formsโ€”for complaints, feedback, wholesale inquiries, you name itโ€”without spending a dime on third-party apps. Itโ€™s cleaner, faster, and gives you total control over the design.

In this guide, weโ€™re going to get our hands dirty (just a little!) and build a professional Complaint/Feedback Page from scratch.


โฑ๏ธ Time to Complete

10 - 15 Minutes

๐ŸŽฏ What Youโ€™ll Achieve

  • ๐Ÿ—๏ธ Create a reusable Custom Section with Liquid.
  • ๐Ÿ”— Connect it to a JSON Page Template.
  • ๐ŸŽจ meaningful Theme Editor controls for non-techies.
  • ๐Ÿš€ Launch a live custom form without a single app.

๐Ÿง  The Strategy: Why "Section + Template" Rocks

In the old days of Shopify, this was hard. Now, it's elegant. By separating the Form Logic (Section) from the Page Structure (Template), you get:

  1. Reusability: Use the same form design on multiple pages.
  2. No Code Edits for Content: Change headings and buttons directly in the Shopify Theme Editor.
  3. Zero Bloat: No external scripts slowing down your site.

Letโ€™s build it. ๐Ÿ”จ


๐Ÿ› ๏ธ Step 1: Create Your Powerhouse Section

First, we need the "engine" of our form. We'll create a new section file that handles the UI and the form submission logic using Shopify's native {% form 'contact' %} tag.

File to create: sections/custom-complaint-form.liquid

Paste this code (I've polished it for you โœจ):

<div class="page-width py-12">
  <div class="grid gap-8">
    {%- if section.settings.heading != blank -%}
      <h2 class="text-3xl font-bold text-center mb-6">{{ section.settings.heading }}</h2>
    {%- endif -%}

    {% form 'contact', class: 'max-w-xl mx-auto space-y-4' %}

      {%- if form.posted_successfully? -%}
        <div class="p-4 bg-green-100 text-green-700 rounded-lg" role="alert">
          โœ… {{ section.settings.success_message | default: 'Thanks! We have received your feedback.' }}
        </div>
      {%- elsif form.errors -%}
        <div class="p-4 bg-red-100 text-red-700 rounded-lg">
          {{ form.errors | default_errors }}
        </div>
      {%- endif -%}

      <!-- Name Field -->
      <div>
        <label for="ContactFormName" class="block text-sm font-medium mb-1">Name</label>
        <input type="text" id="ContactFormName" name="contact[name]"
               class="w-full border rounded p-2"
               placeholder="John Doe"
               required>
      </div>

      <!-- Email Field -->
      <div>
        <label for="ContactFormEmail" class="block text-sm font-medium mb-1">Email</label>
        <input type="email" id="ContactFormEmail" name="contact[email]"
               class="w-full border rounded p-2"
               placeholder="[email protected]"
               required>
      </div>

      <!-- Custom Field: Type of Inquiry -->
      <div>
         <label for="ContactFormType" class="block text-sm font-medium mb-1">Feedback Type</label>
         <select id="ContactFormType" name="contact[feedback_type]" class="w-full border rounded p-2">
           <option>General Feedback</option>
           <option>Product Complaint</option>
           <option>Shipping Issue</option>
           <option>Other</option>
         </select>
      </div>

      <!-- Message Body -->
      <div>
        <label for="ContactFormMessage" class="block text-sm font-medium mb-1">Details</label>
        <textarea rows="5" id="ContactFormMessage" name="contact[body]"
                  class="w-full border rounded p-2"
                  placeholder="Tell us what happened..."
                  required></textarea>
      </div>

      <!-- Submit Button -->
      <button type="submit" class="w-full bg-black text-white font-bold py-3 rounded hover:bg-gray-800 transition">
        {{ section.settings.button_label | default: 'Send Message' }}
      </button>

    {% endform %}
  </div>
</div>

{% schema %}
{
  "name": "Custom Constraint Form",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Form Heading",
      "default": "We Value Your Feedback"
    },
    {
      "type": "text",
      "id": "button_label",
      "label": "Button Label",
      "default": "Submit Feedback"
    },
    {
      "type": "richtext",
      "id": "success_message",
      "label": "Success Message",
      "default": "<p>Thank you. Your submission has been received.</p>"
    }
  ],
  "presets": [
    {
      "name": "Custom Complaint Form"
    }
  ]
}
{% endschema %}

๐Ÿ”ฅ Pro Tip: Notice the name="contact[custom_field_name]" pattern? You can add any field you want (Address, Order ID, Pet's Name) just by following that pattern. Shopify will package it up and email it to you!


๐Ÿ“„ Step 2: The "Glue" (Page Template)

Now we need a page template that uses this section. In Online Store 2.0, we use JSON templates for this.

File to create: templates/page.complaint-feedback.json

{
  "sections": {
    "main": {
      "type": "custom-complaint-form",
      "settings": {
        "heading": "Complaint & Feedback Form",
        "button_label": "Submit Report"
      }
    }
  },
  "order": ["main"]
}

This file tells Shopify: "Hey, when someone renders this page, load the component we just built!"


๐Ÿ”— Step 3: Go Live! (Connect in Admin)

The code is done. ๐ŸŽ‰ Now let's hook it up in the Shopify Admin.

  1. Navigate to Online Store > Pages.
  2. Click Add page.
  3. Title it nicely (e.g., "Feedback Center").
  4. The Magic Step: Look for the Theme template dropdown (usually on the bottom right).
  5. Select complaint-feedback (the template we just made).
  6. Click Save.

boom! ๐Ÿ’ฅ Your custom form is live.


๐Ÿงญ Managing Submissions

"But where do the emails go?" ๐Ÿค”

Since we used the native {% form 'contact' %}, Shopify treats this just like your standard Contact Us page.

  • Notifications: Check your settings in Settings > Notifications > Staff order notifications (or specifically the Contact/Sender email settings).
  • Data: All custom fields (like "Feedback Type") will appear as text key-value pairs in the email body you receive.

๐Ÿšง Common Pitfalls to Avoid

  • โŒ Missing contact[body]: You must have one field named contact[body] or contact[comments] for the main message.
  • โŒ Spaces in Names: detailed fields like contact[My Field] usually work, but contact[my_field] is safer code practice.
  • โŒ Forgotten Schemas: If you don't add the {% schema %}, you won't be able to edit the text in the Theme Editor!

๐Ÿ”ฎ What's Next?

You've just unlocked a superpower. You can use this exact same pattern for:

  • ๐Ÿ“ Wholesale Applications
  • ๐Ÿ‘ท Job Application Forms
  • ๐Ÿ”„ Returns/Warranty Requests

Check out the Shopify Liquid Documentation for more advanced form tags, or explore Tailwind CSS if you want to spice up your styling even more.

Related posts