Deep dive · 5 min read
Merchant-editable coffee pages with Shopify metafields
How purchasable variants stay in Shopify while origin, process, roast, tasting notes, and brewing guidance live in reusable, metafield-driven product sections.
- Metafields
- Product modeling
Picture a customer comparing two single-origin coffees. They are not weighing the price. They are reading the origin, the process, the elevation, the roast profile, and the tasting notes, and deciding which cup they want to wake up to. A general-purpose commerce theme has no concept of those specialty-coffee attributes. It provides the core commerce primitives, but the coffee-specific fields still have to be designed and connected to the product.
That gap is the whole problem this part of the Boxwood build had to solve. Specialty coffee is an information product as much as a physical one, and the storefront needed to carry that information in a way the merchandising team could edit for every new release, without a theme-code change each time.
This deep dive expands the Boxwood Coffee case. Here is how the data was modeled.
Two kinds of information
The first decision was to stop treating "product information" as one thing. On a coffee product there are really two kinds, and one question separates them: does this information change the cart, or does it inform the decision?
Purchasable choices
Grind and weight. These change what goes in the cart, the price, and the inventory count.
Descriptive attributes
Origin, region, elevation, variety, process, roast profile, tasting notes, brewing guidance. These do not change the cart. They inform the decision to buy.
Treating descriptive attributes as variants would create unnecessary option combinations, complicate inventory and pricing logic, and present informational details as purchasable choices. Hardcoding them into the theme layout traps the content in code and puts a developer in the path of every new coffee. So the two kinds were kept apart, each in the tool built for it.
Variants stay variants, attributes become metafields
Grind and weight stayed as native Shopify variant options. That is exactly what they are, and the native product form, cart, and checkout already handle them reliably. I styled the variant selectors to match the brand and left the behavior alone.
The descriptive attributes were stored as product metafields, so they live with the product in Shopify rather than inside the theme layout. They were grouped into four reusable product-page sections.
Coffee data
Reads: origin, region, elevation, variety, process, roast profile
Renders: a structured specification grid
Tasting note
Reads: tasting note
Renders: a prominent tasting line
Origin story
Reads: story image and story text
Renders: an image and short product narrative
Product information
Reads: brewing guidance, roast and shipping details, storage, and subscription information
Renders: collapsible practical guidance
The card badge on collection pages was extended the same way, reading a short label and a tasting line from the product so the coffee vocabulary shows up before the customer even opens the page.
Sections that read the data, not hold it
Each of the four sections follows the same three rules, and those rules are what make the model reusable.
01 · Read from the product
The section pulls the metafield off the current product and renders it; the content is not typed into the section. That is what lets one section serve every coffee in the catalog.
02 · Fall back gracefully
Each field checks the product metafield first, then a section setting, so a product that has not been filled in yet still looks right in the theme editor and never renders an empty label.
Simplified Liquid pattern. Field names and control flow are simplified for readability.
{%- liquid
assign brewing = product.metafields.custom.brewing_recommendations.value
assign brewing_title = product.metafields.custom.brewing_title.value
| default: section.settings.brewing_title
-%}
{%- if brewing != blank -%}
{% render 'accordion', title: brewing_title, content: brewing %}
{%- endif -%}
03 · Render only when data exists
If a coffee has no origin story, the story section renders nothing at all, rather than an empty box. The specification grid checks whether any of its fields have a value before it draws a single row. A product page only shows the parts that have real data behind them.
One more detail worth naming: the collapsible section reuses the theme's own accordion component rather than reinventing it. The custom code supplies the data and the structure; the proven native UI does the opening and closing.
One template is not enough
Not every product is a bag of coffee. A subscription product and a pre-order need a different page than a single-origin lot. Rather than accumulate product-type conditionals inside one increasingly complex template, the store uses separate product templates, assigned per product in the admin.
- Coffee product → full coffee data sections
- Subscription product → leaner subscription layout
- Pre-order product → dedicated pre-order layout
Assigning a template is a dropdown in the product admin, so the merchandising team controls which layout a product gets without a code change. The descriptive sections are assigned only to the relevant product templates, preventing them from rendering on unrelated product types.
What the merchant actually gets
The payoff is a workflow, not a feature. To launch a new seasonal coffee, the Boxwood team:
- creates the product
- adds grind and weight variants
- fills in the coffee metafields (origin, process, roast, tasting notes, brewing, and so on)
- assigns the appropriate template and collection
- publishes
That is the routine launch path. When the new release fits the existing sections and metafields, no theme-code change is required. A limited release and a year-round blend use the exact same sections and render correctly on their own data. Fields that are left empty simply do not appear. Routine seasonal releases became a content workflow rather than a recurring engineering change.
Where the line is
This model is honest about its limits, and I would rather state them than oversell.
Adding a brand-new kind of attribute, say an "altitude range" field that has never existed before, is not self-serve. It means adding a field to a section and defining the metafield, which is developer work. Day-to-day content and new coffee releases that fit the existing model are self-serve. Changing the shape of the model is deliberate and reviewed.
Content is merchant-owned. Changes to the field structure itself remain engineering work.
Takeaway
Split product information by asking one question: does it change the cart, or does it inform the decision? Purchasable choices belong in variants. Descriptive attributes belong in metafields, close to the product, rendered by reusable sections that read the data and hide when it is absent.
Do that, and the theme stops being where content lives and goes back to being what it should be: the thing that renders the content. For a specialty brand launching coffees throughout the year, that difference separates a routine product-data workflow from a recurring theme change.
Need a Shopify build that knows when custom code is worth it?
I adapt, extend, and build around real catalog, content, and platform constraints, while keeping purchase-critical systems reliable and maintainable.
Start a conversation