Collect custom customer attributes

Build a simple form to collect custom customer attributes, store them into a customer metafield and optionally sync them with Klaviyo.
Demo (screen recording)
Syncing with Klaviyo is only available for customer type resource

Steps

  1. 1.
    Create a Raven to carry customer attributes payload, if you want to sync with Klaviyo, select "Sync with Klaviyo" and add Klaviyo private API key (demo).
  2. 2.
    Add raven id to raven-mac-gen liquid code snippet along with resource_id
  3. 3.
    Write a little bit of Liquid, HTML, CSS, and Javascript.
  4. 4.
    🎉
I used Tailwind CSS to style the form and AlpineJS to manage the state of the form and UI updates.

Code

{% if customer %}
<div class="form-state-wrapper"
x-data="{
test_field: null,
customerAttrs: {
sidedish: 'None',
birthDay: null,
birthMonth: null
},
hasError: false,
isSubmitted: false,
isValid() {
return !Object.values(this.customerAttrs).some(value => value === null || value === '')
},
submitData() {
console.log('this.isValid() ->', this.isValid());
console.log('JSON.stringify(this.customerAttrs) ->', JSON.stringify(this.customerAttrs));
if (this.isValid()) {
const ravenObj = {%- render 'raven-mac-gen', resource_id: customer.id, raven_id: 'TBD' -%};
const valueObj = { value: JSON.stringify(this.customerAttrs) };
const response = Raven.send(ravenObj, valueObj);
response.then(res => {
if (res.status === 200) {
console.log('🎉', res.json)
this.hasError = false;
this.isSubmitted = true;
console.log('let us be friends 🎉👬');
} else {
console.error('😞', res)
}
})
.catch(e => console.error(e));
} else {
this.isSubmitted = false;
this.hasError = true;
}
}
}"
>
<template x-if="isSubmitted">
<div class="tw-rounded-md tw-bg-green-50 tw-p-4 tw-mb-8">
<div class="tw-flex">
<div class="tw-flex-shrink-0">
<svg class="tw-h-5 tw-w-5 tw-text-green-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd" />
</svg>
</div>
<div class="tw-ml-3">
<h3 class="tw-text-xl tw-font-medium tw-text-green-800">Thank you, we truly appreciate your friendship 🙌🙏 We'll be in touch!</h3>
</div>
</div>
</div>
</template>
<template x-if="hasError">
<div class="tw-rounded-md tw-bg-red-50 tw-p-4 tw-mb-8">
<div class="tw-flex">
<div class="tw-flex-shrink-0">
<svg class="tw-h-5 tw-w-5 tw-text-red-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd" />
</svg>
</div>
<div class="tw-ml-3">
<h3 class="tw-text-xl tw-font-medium tw-text-red-800">Please select proper values so we can get this friendship started! 🎉👬</h3>
</div>
</div>
</div>
</template>
<template x-if="!isSubmitted">
<div id="form-wrapper">
<code>
{{ customer.metafields.klaviyo_sync.lets_be_friends.value }}
</code><br>
<div id="birthdate-wrapper">
<fieldset class="tw-mb-8 tw-bg-white tw-max-w-xs">
<legend class="tw-block tw-text-xl tw-font-medium tw-text-gray-700 tw-mb-4">Care to share your birthday?</legend>
<div class="tw-mt-1 tw-rounded-md tw-shadow-sm tw-space-y-px">
<div>
<label for="birth-day" class="tw-sr-only">Day</label>
<select x-model="customerAttrs.birthDay" id="birth-day" name="birth-day" class="focus:tw-ring-indigo-500 focus:tw-border-indigo-500 tw-relative tw-block tw-w-full tw-rounded-none tw-rounded-t-md tw-bg-transparent focus:tw-z-10 sm:tw-text-xl tw-border-gray-300">
<option value="" selected>Select day</option>
{% for day in (1..31) %}
<option value="{{ day }}">{{ day }}</option>
{% endfor %}
</select>
</div>
<div>
{% assign months = "January, February, March, April, May, June, July, August, September, October, November, Decembe" | split: ',' %}
<label for="birth-month" class="tw-sr-only">Month</label>
<select x-model="customerAttrs.birthMonth" id="birth-month" name="birth-month" class="focus:tw-ring-indigo-500 focus:tw-border-indigo-500 tw-relative tw-block tw-w-full tw-rounded-none tw-rounded-b-md tw-bg-transparent focus:tw-z-10 sm:tw-text-xl tw-border-gray-300">
<option value="" selected>Select month</option>
{% for month_raw in months %}
{% assign month = month_raw | strip %}
<option value="{{ month }}">{{ month }}</option>
{% endfor %}
</select>
</div>
</div>
</fieldset>
</div>
<div id="sidedish-wrapper">
<fieldset class="mt-4">
<legend class="tw-block tw-text-xl tw-font-medium tw-text-gray-700 tw-mb-4">What's your favorite side dish?</legend>
<div class="tw-space-y-4 sm:tw-flex sm:tw-items-center sm:tw-space-y-0 sm:tw-space-x-10">
<div class="tw-flex tw-items-center">
<input x-model="customerAttrs.sidedish" value="None" id="sidedish-none" name="favorite-sidedish" type="radio" checked class="focus:tw-ring-indigo-500 tw-h-4 tw-w-4 tw-text-indigo-600 border-gray-300">
<label for="sidedish-none" class="tw-ml-3 tw-block tw-text-xl tw-font-medium tw-text-gray-700"> None </label>
</div>
<div class="tw-flex tw-items-center">
<input x-model="customerAttrs.sidedish" value="Baked beans" id="sidedish-baked-beans" name="favorite-sidedish" type="radio" class="focus:tw-ring-indigo-500 tw-h-4 tw-w-4 tw-text-indigo-600 border-gray-300">
<label for="sidedish-baked-beans" class="tw-ml-3 tw-block tw-text-xl tw-font-medium tw-text-gray-700"> Baked beans </label>
</div>
<div class="tw-flex tw-items-center">
<input x-model="customerAttrs.sidedish" value="Coleslaw" id="sidedish-coleslaw" name="favorite-sidedish" type="radio" class="focus:tw-ring-indigo-500 tw-h-4 tw-w-4 tw-text-indigo-600 border-gray-300">
<label for="sidedish-coleslaw" class="tw-ml-3 tw-block tw-text-xl tw-font-medium tw-text-gray-700"> Coleslaw </label>
</div>
<div class="tw-flex tw-items-center">
<input x-model="customerAttrs.sidedish" value="French fries" id="sidedish-frenchfries" name="favorite-sidedish" type="radio" class="focus:tw-ring-indigo-500 tw-h-4 tw-w-4 tw-text-indigo-600 border-gray-300">
<label for="sidedish-frenchfries" class="tw-ml-3 tw-block tw-text-xl tw-font-medium tw-text-gray-700"> French fries </label>
</div>
<div class="tw-flex tw-items-center">
<input x-model="customerAttrs.sidedish" value="Garden salad" id="sidedish-gardensalad" name="favorite-sidedish" type="radio" class="focus:tw-ring-indigo-500 tw-h-4 tw-w-4 tw-text-indigo-600 border-gray-300">
<label for="sidedish-gardensalad" class="tw-ml-3 tw-block tw-text-xl tw-font-medium tw-text-gray-700"> Garden salad </label>
</div>
<div class="tw-flex tw-items-center">
<input x-model="customerAttrs.sidedish" value="Mashed potatoes" id="sidedish-mashed-potatoes" name="favorite-sidedish" type="radio" class="focus:tw-ring-indigo-500 tw-h-4 tw-w-4 tw-text-indigo-600 border-gray-300">
<label for="sidedish-mashed-potatoes" class="tw-ml-3 tw-block tw-text-xl tw-font-medium tw-text-gray-700"> Mashed potatoes </label>
</div>
</div>
</fieldset>
</div>
<button @click="submitData()" type="button" class="tw-mt-8 tw-inline-flex tw-items-center tw-px-4 tw-py-2 tw-border tw-border-transparent tw-text-xl tw-font-medium tw-rounded-md tw-shadow-sm tw-text-white tw-bg-indigo-600 hover:tw-bg-indigo-700 focus:tw-outline-none focus:tw-ring-2 focus:tw-ring-offset-2 focus:tw-ring-indigo-500">Let's be friends!</button>
</div>
</template>
{% else %}
<div class="tw-bg-white tw-border-gray-200 tw-shadow-sm tw-rounded-lg tw-border tw-p-4">
<p>Friends share emails together 👬 Please <a class="tw-text-indigo-600 tw-whitespace-nowrap hover:tw-text-indigo-500" href="/account/login">log in</a> or <a class="tw-text-indigo-600 tw-whitespace-nowrap hover:tw-text-indigo-500" href="/account/register">register</a> so we can be better friends 🙌</p>
</div>
{% endif %}