EU Withdrawal Button for Shopify

Add the EU's two-step “withdraw from contract here” function to Shopify: a page section, a footer link and the acknowledgement email.

Describe the result you need. AI will set up the tool and ask for any missing details.

Configure with AI

Enter to submit · Shift + Enter for a new line

Sent to AI. Leave out passwords and private customer data.

Your details

Output

EU law sets at least 14 days. Use your own period if it's longer.

days

The page lives at /pages/ plus this handle.

Form

Your code

withdrawal-form.liquid
{%- comment -%}
  EU right of withdrawal: a two-step withdrawal function (Article 11a, Directive 2011/83/EU as amended
  by Directive (EU) 2023/2673). Step 1 opens the online withdrawal statement, step 2 confirms it.
  Submissions arrive through Shopify's contact form, at your store's contact email.
{%- endcomment -%}
{%- assign form_id = 'WithdrawalForm-' | append: section.id -%}

{%- style -%}
  #Withdrawal-{{ section.id }}{display:block;padding:2.5rem 1rem}
  #Withdrawal-{{ section.id }} .ea-wd__inner{max-width:560px;margin:0 auto}
  #Withdrawal-{{ section.id }} .ea-wd__heading{margin:0 0 .75rem;font-size:1.6em;line-height:1.25}
  #Withdrawal-{{ section.id }} .ea-wd__intro{margin:0 0 1.25rem;line-height:1.6}
  #Withdrawal-{{ section.id }} .ea-wd__button{display:inline-block;padding:.85em 1.5em;border:0;border-radius:6px;background:{{ section.settings.button_background }};color:{{ section.settings.button_text }};font:inherit;font-weight:600;cursor:pointer;list-style:none}
  #Withdrawal-{{ section.id }} summary.ea-wd__button::-webkit-details-marker{display:none}
  #Withdrawal-{{ section.id }} .ea-wd__details[open] summary.ea-wd__button{opacity:.75}
  #Withdrawal-{{ section.id }} .ea-wd__fields{display:grid;gap:1rem;margin-top:1.25rem}
  #Withdrawal-{{ section.id }} label{display:grid;gap:.35rem;font-weight:600;font-size:.95em}
  #Withdrawal-{{ section.id }} input,#Withdrawal-{{ section.id }} textarea{font:inherit;font-weight:400;padding:.7em .8em;border:1px solid rgba(0,0,0,.25);border-radius:6px;width:100%;box-sizing:border-box;background:#fff;color:#1a1a1a}
  #Withdrawal-{{ section.id }} .ea-wd__hint{font-weight:400;font-size:.85em;opacity:.75}
  #Withdrawal-{{ section.id }} .ea-wd__errors{padding:.75em 1em;border-radius:6px;background:#fcebea;color:#7f1d17}
  #Withdrawal-{{ section.id }} .ea-wd__success{padding:1.25em;border-radius:8px;background:#eaf5ef;color:#174f35;outline:none}
  #Withdrawal-{{ section.id }} .ea-wd__success p{margin:0 0 .5em}
  #Withdrawal-{{ section.id }} .ea-wd__success-heading{font-weight:700;font-size:1.1em}
{%- endstyle -%}

<ea-withdrawal id="Withdrawal-{{ section.id }}" class="ea-wd">
  <div class="ea-wd__inner">
    <h2 class="ea-wd__heading">{{ section.settings.heading | escape }}</h2>
    {%- form 'contact', id: form_id, class: 'ea-wd__form' -%}
      {%- if form.posted_successfully? -%}
        <div class="ea-wd__success" role="status" tabindex="-1">
          <p class="ea-wd__success-heading">{{ section.settings.success_heading | escape }}</p>
          <p>{{ section.settings.success_text | escape | replace: '[date]', '<span data-timestamp></span>' }}</p>
        </div>
      {%- else -%}
        {%- if section.settings.intro != blank -%}
          <p class="ea-wd__intro">{{ section.settings.intro | escape | newline_to_br }}</p>
        {%- endif -%}
        <details class="ea-wd__details"{% if form.errors %} open{% endif %}>
          <summary class="ea-wd__button">{{ section.settings.withdraw_label | escape }}</summary>
          <div class="ea-wd__fields">
            {%- if form.errors -%}
              <div class="ea-wd__errors">{{ form.errors | default_errors }}</div>
            {%- endif -%}
            <input type="hidden" name="contact[Request]" value="Withdrawal">
            <input type="hidden" name="contact[body]" value="{{ section.settings.statement | escape }}">
            <label>
              {{ section.settings.name_label | escape }}
              <input type="text" name="contact[Name]" autocomplete="name" required value="{% if customer %}{{ customer.name | escape }}{% endif %}">
            </label>
            <label>
              {{ section.settings.email_label | escape }}
              <input type="email" name="contact[email]" autocomplete="email" required value="{% if form.email %}{{ form.email | escape }}{% elsif customer %}{{ customer.email | escape }}{% endif %}">
            </label>
            <label>
              {{ section.settings.order_label | escape }}
              <input type="text" name="contact[Order number]" required data-order-field>
            </label>
            {%- if section.settings.show_items -%}
              <label>
                {{ section.settings.items_label | escape }}
                <textarea name="contact[Items]" rows="3"></textarea>
                <span class="ea-wd__hint">{{ section.settings.items_hint | escape }}</span>
              </label>
            {%- endif -%}
            <div>
              <button type="submit" class="ea-wd__button">{{ section.settings.confirm_label | escape }}</button>
            </div>
          </div>
        </details>
      {%- endif -%}
    {%- endform -%}
  </div>
</ea-withdrawal>

<script>
  if (!customElements.get('ea-withdrawal')) {
    customElements.define('ea-withdrawal', class extends HTMLElement {
      connectedCallback() {
        const stamp = this.querySelector('[data-timestamp]');
        if (stamp) {
          const now = new Date();
          try {
            stamp.textContent = now.toLocaleString(document.documentElement.lang || undefined, { dateStyle: 'long', timeStyle: 'short' });
          } catch (e) {
            stamp.textContent = now.toLocaleString();
          }
          const box = this.querySelector('.ea-wd__success');
          if (box) box.focus();
        }
        // Fill the order number from a link such as /pages/withdrawal?order=1042
        const order = new URLSearchParams(window.location.search).get('order');
        const field = this.querySelector('[data-order-field]');
        if (order && field && !field.value) field.value = order.slice(0, 40);
      }
    });
  }
</script>

{% schema %}
{
  "name": "Withdrawal form",
  "tag": "section",
  "class": "ea-withdrawal-section",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "default": "Withdraw from your purchase"
    },
    {
      "type": "textarea",
      "id": "intro",
      "label": "Intro",
      "default": "You can withdraw from your purchase within 14 days of receiving it, without giving a reason. Fill in the form below and confirm. We'll email you an acknowledgement of receipt."
    },
    {
      "type": "text",
      "id": "withdraw_label",
      "label": "Withdrawal button",
      "default": "Withdraw from contract here",
      "info": "EU law asks for this wording or an equally unambiguous one."
    },
    {
      "type": "text",
      "id": "confirm_label",
      "label": "Confirmation button",
      "default": "Confirm withdrawal",
      "info": "EU law asks for this wording or an equally unambiguous one."
    },
    {
      "type": "header",
      "content": "Form"
    },
    {
      "type": "text",
      "id": "name_label",
      "label": "Name label",
      "default": "Full name"
    },
    {
      "type": "text",
      "id": "email_label",
      "label": "Email label",
      "default": "Email for the confirmation"
    },
    {
      "type": "text",
      "id": "order_label",
      "label": "Order number label",
      "default": "Order number"
    },
    {
      "type": "checkbox",
      "id": "show_items",
      "label": "Ask which items",
      "default": true
    },
    {
      "type": "text",
      "id": "items_label",
      "label": "Items label",
      "default": "Items you're withdrawing from (optional)"
    },
    {
      "type": "text",
      "id": "items_hint",
      "label": "Items hint",
      "default": "Leave empty to withdraw from the whole order."
    },
    {
      "type": "text",
      "id": "statement",
      "label": "Withdrawal statement",
      "default": "I hereby withdraw from my contract for the order below.",
      "info": "Sent to you with each form."
    },
    {
      "type": "header",
      "content": "After sending"
    },
    {
      "type": "text",
      "id": "success_heading",
      "label": "Success heading",
      "default": "We've received your withdrawal"
    },
    {
      "type": "textarea",
      "id": "success_text",
      "label": "Success text",
      "default": "Your withdrawal statement was sent on [date]. We'll email an acknowledgement of receipt to the address you gave.",
      "info": "[date] becomes the date and time the form was sent."
    },
    {
      "type": "header",
      "content": "Colors"
    },
    {
      "type": "color",
      "id": "button_background",
      "label": "Button",
      "default": "#1a1a1a"
    },
    {
      "type": "color",
      "id": "button_text",
      "label": "Button text",
      "default": "#ffffff"
    }
  ],
  "presets": [
    {
      "name": "Withdrawal form"
    }
  ]
}
{% endschema %}

Install: add this file under Sections (Online Store → Themes → … → Edit code → Add a new section, named withdrawal-form). Create a page at Online Store → Pages with the handle withdrawal. In Customize, switch to Pages → Create template, add the Withdrawal form section, then assign that template to the page.

Link to the page from every page of your store (see Footer link above) so the function stays easy to find for the whole withdrawal period.

Requests arrive at your store's contact email. You still have to send the customer an acknowledgement with the content, date and time of their request without undue delay (see Acknowledgement email above).

This isn't legal advice. It follows Article 11a of the Consumer Rights Directive as added by Directive (EU) 2023/2673, which applies from 19 June 2026. National laws can set their own wording or extra duties, so check yours or ask a lawyer.

Live preview

What the law asks for

Directive (EU) 2023/2673 adds Article 11a to the Consumer Rights Directive. Member States apply it from 19 June 2026. For contracts made through an online interface, you must offer a withdrawal function:

  • Step 1: a function labelled “withdraw from contract here” (or an unambiguous equivalent), continuously available during the withdrawal period, prominently displayed and easy to reach.
  • The statement: it lets the consumer give or confirm their name, details identifying the contract, and the electronic means for your confirmation.
  • Step 2: a confirmation function labelled only “confirm withdrawal” (or an unambiguous equivalent).
  • Acknowledgement: after they confirm, you send an acknowledgement of receipt on a durable medium, including its content and the date and time it was submitted, without undue delay.

This isn't legal advice. National laws can set their own wording or add duties. Check your country's rules or ask a lawyer.

What the generator gives you

  1. 01

    Withdrawal page section

    A withdrawal-form section: a “withdraw from contract here” button that opens the form, then a “confirm withdrawal” button that sends it through Shopify's contact form. Logged-in customers get their name and email filled in.

  2. 02

    Footer link

    A Custom Liquid snippet that shows the link in the shopper's language and keeps the language in the URL.

  3. 03

    Acknowledgement email

    Text for the receipt you send back, with the statement's content and the date and time.

Link to the page as /pages/withdrawal?order=1042 from your order emails and the order number is filled in. The notification email template generator can add that link for you.

The official button wording

These are the labels in each language version of the Directive. The form's other texts are our translations; have a native speaker check them.

LanguageWithdrawal functionConfirmation function
Englishwithdraw from contract hereconfirm withdrawal
GermanVertrag widerrufenWiderruf bestätigen
Frenchrenoncer au contrat iciconfirmer la rétractation
Spanishdesistir del contrato aquíconfirmar desistimiento
Italianrecedere dal contratto quiconferma recesso
Dutchhier de overeenkomst herroepenherroeping bevestigen
Polishodstąp od umowy tutajpotwierdź odstąpienie od umowy
Portugueseretrate-se do contrato aquiconfirmar retratação
Swedishångra avtalet härbekräfta frånträde
DanishFortryd aftaleBekræft fortrydelse
FinnishPeruuta sopimus tästäVahvista peruuttaminen
Czechzde odstoupit od smlouvypotvrdit odstoupení od smlouvy
Romanianretrageți-vă din contract aiciconfirmați retragerea
Hungarianelállás a szerződéstőlelállás megerősítése
GreekΠατήστε εδώ για υπαναχώρησηεπιβεβαίωση υπαναχώρησης

Setting it up on Shopify

  1. 01

    Add the section

    In Online Store → Themes → … → Edit code, add a new section named withdrawal-form and paste the section code.

  2. 02

    Create the page

    In Online Store → Pages, add a page with the handle withdrawal.

  3. 03

    Give it a template

    In Customize, switch to Pages → Create template, add the Withdrawal form section, then assign the template to the page.

  4. 04

    Link it everywhere

    Add the footer link, and the link in your order confirmation email.

  5. 05

    Set up the acknowledgement

    Shopify's contact form doesn't reply to the customer. Send the acknowledgement from your help desk as an auto-reply, or by hand as soon as each request arrives.

For stores in several languages, translate the section's settings in Shopify's Translate & Adapt app, using the table above for the two buttons.

Questions people ask

When does the EU withdrawal button become mandatory?

Member States apply the new rule from 19 June 2026, under Directive (EU) 2023/2673.

Does Shopify add the withdrawal button automatically?

Check your theme and admin first. If Shopify or an app already provides one, don't add a second.

Where do withdrawal requests go?

Through Shopify's contact form, to your store's contact email, with the name, email, order number, items and the withdrawal statement.

Is the button enough to comply?

No. You also need to acknowledge each withdrawal on a durable medium, such as email, and handle refunds within the legal deadlines. This isn't legal advice.

Can I change the button wording?

The law allows an “unambiguous corresponding formulation”, but the Directive's own wording is the safest choice. National laws may be stricter.

Want this builtinto your store?

I'm Emmanuel, a Shopify and WooCommerce developer. If this tool pointed at something you'd rather have fixed for you (a faster store, cleaner product data, a migration, a custom feature), send me a note.

Emmanuel Asika

Emmanuel Asika

Shopify & WooCommerce developer, Dublin

I build and fix online stores: new builds, migrations, speed, SEO and the custom bits apps can't do.

hello@emmanuelasika.com