← Back to Archive

Offering Gift Packaging via Checkout UI Extension


Building a UI Extension that offers a simple custom Gift message and a material choice.


This is a little open-source Gift Packaging extension I've built, with some neat considerations within it. I think it nicely highlights some of the flexibility and optionality you can bring into the Checkout for customers to interact with when it makes sense to do so.

There are many small quality-of-life pieces in this extension that makes it a really workable solution for a lot of different potential use cases.

Showing a custom Checkout UI Extension that offers a gift message entry, plus a selection of materials.

WHAT DOES THIS ACTUALLY DO?

Good place to start, thanks for asking. There are so many little things this compact UI Extension does:

  • It's collapsed by default so that it's unobtrusive to a customer until they decide to interact with the gift option selection.
  • Once it's expanded, it can support a Sender's custom name and a Reciepient's custom name, both of which are capped at 25 characters, and a custom message, which are all taken as text strings.
  • Those custom text strings are stored as Metafields on the Order object after the checkout is completed.
  • If the customer de-selects the Gift Packaging option and closes the UI, all those entries are stripped and the metafields are wiped so that the Order returns to normal with no errant data stored.
  • There's also a Material selection option. I've only included two, but it could be expanded. The extension also stores that selection as a Metafield on the Order, so it can be packed appropriately.
  • I've also made sure that every field is completely customisable by the store staff adding the extension - everything for the section title, the placeholder text, the image previews for the material choices.

HOW IS THE EXTENSION BEING CUSTOMISED THROUGH THE CHECKOUT EDITOR?

Gift UI Extension, annotated with arrows.

That's possible by defining new [[extensions.settings.fields]] within the Extension project file. One of the settings options to allow a user to modify the title of the UI Extension in the editor looks like this:

shopify.extension.toml
[[extensions.settings.fields]]
key = "section_title"
type = "single_line_text_field"
name = "Section Title"
description = "Define this section's title."

Once that's in place, what we can then do within the Checkout.jsx is use the fields within the Extension's settings to define constants to use when the Extension renders. In this case, I've also defined placeholder defaults if no settings have been customised.

checkout.jsx
function App() {
  const {
    OptionTwo: merchantOptionTwo, 
    image_two: merchantImageTwo,
    OptionOne: merchantOptionOne, 
    image_one: merchantImageOne,
    choice_title: merchantChoiceTitle,
    section_title: merchantSectionTitle,
    section_prompt: merchantSectionPrompt,
    text_field_desc: merchantTextFieldDescription,
  } = useSettings();

  const OptionOne = merchantOptionOne ?? 'Option One';
  const OptionTwo = merchantOptionTwo ?? 'Option Two';
  const choice_title = merchantChoiceTitle ?? 'Choice Title';
  const section_title = merchantSectionTitle ?? 'Section Title';
  const section_prompt = merchantSectionPrompt ?? 'Section Prompt';
  const text_field_desc = merchantTextFieldDescription ?? 'Text Field Description';

AFTER THE CHECKOUT, HOW DO I KNOW WHAT A CUSTOMER CHOSE?

Allowing the UI Extension to create and modify Metafields, we can easily store custom data on the Order that any staff or even any downstream connected system could use to both understand and action the Gift selections chosen at Checkout.

The Extensions's metafields as seen on a completed Order.

Those Metafields are set through the React Hooks that are available to UI Extensions, specifically useApplyMetafieldsChange which can be found here in the dev documentation. It looks like this when defining what metafields to set for which textfield or selection entries in the Extension's code:

checkout.jsx
// Set a function to handle updating metafields
const applyMetafieldsChange = useApplyMetafieldsChange();

// Update the metafields when any of the inputs change
const updateMetafields = () => {
  applyMetafieldsChange({
    type: "updateMetafield",
    namespace: metafieldNamespace,
    key: senderNameKey,
    valueType: "string",
    value: senderName,
  });
};

Then when the Extension components are being returned:

checkout.jsx
<TextField
  label="Sender's Name"
  placeholder="Enter sender's name"
  maxLength={25}
  onChange={(value) => {
  setSenderName(value);
  updateMetafields();
  }}
  value={senderName}
  />

I've been a little lazy in my code, and right now I'm just letting the Extension set what's called an 'unstructured' metafield, which just means it hasn't been defined yet within the store. If anyone uses this demo version of the Extension, they can simply make a definition for the 'unstructured' metafields that have been set, and then the Extension will write it's custom data to that new Metafield definition going forward.

Basically, I'm not doing the smart thing and making these 'app-only' Metafields that are pre-defined and are for specific use of the app installing the Extension. They're just loose and feral chunks of data, until they're tamed by a custom metafield definition.

GIFT PACKAGING COSTS ME MONEY, HOW WOULD I CHARGE FOR THE SELECTIONS?

Hmm, yep - I see what you mean. There are a couple of ways to go about it.

Option One

Use the Checkout UI Extension to add an additional line item to the cart based on the customer's selection to make the order a 'Gift'. Most often, this would be supported by having a 'Gift Wrapping' product set up, with maybe variants for different prices depending on selections. Then you would add the proper variant to the cart that corresponds to the selected options. The path to interacting with cart lines is through useApplyCartLinesChange in the UI Extension API.

This would be my preferred method, because the same UI Extension can both take the customer selection, store the selected option, and then add a cart line as a direct result of that selection. It'd all be self-contained, unlike...

Option Two

Take a bit of Option One and add a generic Gift Wrapping line item to the cart. Then, set a cart attribute via the UI Extension for a Cart Transform Function to pick up and execute some pricing logic on the new line item as a result of the attribute being set. This would be for extremely custom pricing calculations based on more complex gifting requirements.

I've written about this method before in "Adjust Prices via Checkout UI Extension".


That's about it! What I love about designing little tools like this is the opportunity to consider the customer experience along the way. Descisions such as how gift material options should be displayed, or how a staff member should be able to interact with a UI Extension block really make the difference between a successful long-term solution and a one-off demo hack job.

You can find the full codebase for the Gift Packaging Extension block here: https://github.com/mackiec/mackiec-extensions-prod/tree/main/extensions/gift-packaging

✌️