Why Shopify doesn't handle this natively
Shopify's variant system was designed for fixed SKU pricing — a Small Red Shirt is $25, a Medium Blue Shirt is $27, period. It wasn't designed for "base price + optional add-ons." Adding a $5 gift wrap fee that customer can toggle on/off doesn't map to variants (would double the SKU count for every product with the option) and doesn't map to line-item properties (which are informational only, don't affect price).
Three practical approaches emerged. Only one is the modern right answer.
Approach 1: Cart Transform Functions (the modern right answer)
Cart Transform is Shopify's server-side function API (2023+) that runs at checkout to modify cart lines — add fees, split, merge. It reads the customer's line-item properties (e.g. "Gift Wrap: yes") and dynamically adds the fee to the cart line's price.
Pros:
- Clean cart line — "Custom Mug: $25" then a separate line "Gift Wrap: $5"
- No hidden products cluttering your catalog
- Discount codes work correctly (or intentionally excluded via flag)
- Analytics reports remain clean (one SKU per variant, not a hidden fee product)
- Scales infinitely — one Cart Transform rule can price options across your entire catalog
Cons:
- Native only on Shopify Plus (non-Plus stores need an app that replicates the behavior)
- Requires Function extension deploy via Shopify CLI if building custom
Personalizer apps like Print It My Way ($7.99+/mo), Bold Product Options ($14.99+/mo), and Hulk Product Options ($8.90+/mo) all provide Cart Transform-equivalent pricing that works on non-Plus stores. Customer experience is identical.
Approach 2: Hidden fee products (deprecated — avoid)
Old pattern: create a hidden product called "Engraving Fee $5" with a variant per price point. When customer selects engraving, the personalizer app adds this hidden product to cart alongside the main product.
Why to avoid:
- Clutters your product catalog with 5-10 fee products
- Shows up as a separate line in checkout: "1 × Cotton T-Shirt $25, 1 × Engraving Fee $5" — visually messy
- Breaks discount codes — "10% off" applies to the fee product too
- Breaks reports — fees appear as separate SKUs, distorting AOV + product performance analytics
- Breaks POD partner sync — hidden fee products may sync to POD partner and cause errors
Only use if you have a very old personalizer app that doesn't support Cart Transform yet.
Approach 3: Variant per fee (usable for finite tiers)
Create a "Standard" variant at $25 and an "Engraved" variant at $30. Customer picks the variant.
Pros: Simple, native Shopify, no app needed.
Cons: Doubles (or triples, or 10x) your variant count for every extra option. A t-shirt with 5 sizes × 8 colors × Standard/Engraved = 80 variants instead of 40. Add rush production and gift wrap and you're at 320 variants. Not scalable past 2-3 options with fees.
Use only when the fee corresponds to a truly different product (e.g. "Silver Chain" vs "Gold Chain" is variant-worthy; "+ engraving" is not).
Setup with Print It My Way (Cart Transform on any plan)
- Install Print It My Way (Free plan supports Cart Transform on 1 personalizer)
- Dashboard → New Personalizer
- Add the field customers will select (e.g. Checkbox "Add gift wrap" or Text "Engraving name")
- Click the field → Pricing tab
- Choose pattern: Per-option flat, Per-character, Percentage of base, or Tiered quantity
- Set the fee amount
- Preview updates cart in real time
- Attach personalizer to product
- Test order: cart should show fee as clean separate line
Pricing pattern examples
Pattern 1: Per-option flat fee (Gift Wrap +$5)
Best for: yes/no add-ons where the fee is uniform. Cart Transform rule: if Gift Wrap = "yes", add $5 to line price.
Pattern 2: Per-character pricing (Engraving after 10 free chars)
Best for: text personalization where surface size limits characters. Cart Transform rule: if Engraving Text exists, compute Math.max(0, chars - 10) * 0.50 and add to line price. Live counter shows customer "5 extra chars = +$2.50" as they type.
Pattern 3: Percentage of base (Rush Production +20%)
Best for: fees that should scale with product value. $200 necklace + 20% rush = $40 rush fee. $1500 wedding ring + 20% rush = $300 rush fee. Cart Transform reads variant base price and computes percentage.
Pattern 4: Tiered quantity (Volume Discount)
Best for: bulk pricing. 1-9 units = full price, 10-49 = -10%, 50+ = -20%. Cart Transform reads cart quantity across matching lines and applies discount.
Pattern 5: Combo discount ($5 off when both Engraving + Gift Wrap selected)
Best for: rewarding customers who buy multiple upgrades. Cart Transform: if both flags true, subtract $5 from line. Shows in cart as "Combo discount: -$5".
How the fee displays at checkout
With Cart Transform (Print It My Way pattern):
- Cart page: Product line + separate fee lines, each with description ("Gift wrap: +$5", "Engraving 'Emma': +$2.50")
- Checkout page: Same clean breakdown, preserved through payment
- Order confirmation email: Fees listed as line items with clear labels
- Merchant admin order view: Fees visible in the order breakdown
With hidden fee products (old pattern):
- Cart: Confusing — "1 × Cotton T-Shirt $25, 1 × Engraving Fee $5" as two separate products
- Checkout: Same messiness
- Analytics: Engraving Fee shows as a product with sales; distorts your product performance reports
Discount code handling
By default, Cart Transform fees are subject to discount codes just like the base price. If a merchant runs "20% off everything" and you added a $5 gift wrap fee, the customer pays $5 - 20% = $4 for the wrap.
Usually that's what you want. But sometimes you want to exclude fees from discounts — for example, a rush production fee is a real cost you can't discount without losing margin. To exclude:
- In Print It My Way's field pricing settings, toggle "Exclude from discount codes"
- The app adds an underscore-prefixed attribute (
_discount_excluded) that Shopify's Discount Function skips when computing discounts - Requires a paired Discount Function on the store (auto-installed by Print It My Way on non-Plus)
App comparison for fee pricing
| App | Fee pricing patterns | Entry price | Free plan? |
|---|---|---|---|
| Print It My Way | All 5 patterns (flat, per-char, percentage, tiered, combo) | $7.99/mo Basic | Yes — 1 personalizer |
| Bold Product Options | Flat, percentage, tiered | $14.99/mo Basic | Trial only |
| Hulk Product Options | Flat, tiered | $8.90/mo | Limited free |
| Zepto Product Personalizer | Flat, per-char | $9.99/mo | Trial only |
| Zakeke | Flat, percentage | ~$29.99/mo | Trial only |
| Kickflip | Flat, tiered (+ 1.95% transaction fee) | Subscription + 1.95% | Trial only |
Tax handling on fees
Cart Transform fees inherit the tax rate of the parent product's cart line. If the product is subject to 8% sales tax, the fee is too. No separate tax configuration needed.
For EU VAT stores, the fee is treated as part of the same supply — same VAT rate as the product. Verify with your accountant that this matches your VAT classification for optional accessories vs core product.
Common issues + fixes
| Issue | Cause | Fix |
|---|---|---|
| Fee not applying at cart | Cart Transform rule inactive or misconfigured | Personalizer dashboard → Cart Transform logs → verify recent runs |
| Fee applies twice on same line | Function ran without idempotency check | App-side fix; report to app developer if persistent |
| Discount code makes fee negative | Discount larger than base + fee combined | Set minimum cart threshold on discount code |
| Fee doesn't show in checkout summary | Old theme's cart template missing line breakdown | Upgrade theme to Online Store 2.0 (Dawn) |
| Fee amount looks wrong on multi-currency store | Fee not converting to customer's currency | Personalizer must use variant's cost.amountPerQuantity, not a hardcoded amount |
Frequently asked questions
How do I charge extra for an option on Shopify?
Use Cart Transform via a personalizer app (Print It My Way, Bold, Hulk). Configure the fee as per-option flat, per-character, percentage, or tiered. Fee shows as clean cart line at checkout.
Is Cart Transform Plus-only?
Native Cart Transform is Plus-only. Personalizer apps provide equivalent behavior on non-Plus stores. Customer experience identical.
Should I create a hidden fee product?
No — deprecated pattern. Clutters catalog, breaks discount codes, breaks reports. Use Cart Transform instead.
Best pricing pattern for gift wrap?
Per-option flat fee ($5 typical). Cart Transform rule: if Gift Wrap = "yes", add $5. 40-50% take rate on gift orders.
Best pattern for engraving?
Per-character. First 10 chars free, $0.50-1.00/char after (depends on surface). Live counter shows fee as customer types.
Best pattern for rush production?
Percentage (15-25% of base) so fee scales with product value. Or flat if all products cost similar.
How to exclude fees from discount codes?
Print It My Way field settings → "Exclude from discount codes" toggle. Adds hidden attribute; paired Discount Function skips fees.
Do fees inherit product tax rate?
Yes. Cart Transform fees taxed at same rate as parent product's cart line. No separate tax config.
Multi-currency store — do fees convert?
Yes if personalizer uses cost.amountPerQuantity from variant instead of hardcoded amount. Print It My Way handles this correctly.
Best free app for option pricing?
Print It My Way Free plan — 1 personalizer, all 5 pricing patterns, no trial expiry. Alternative: Hulk Product Options limited free tier.
Related setup guides
- Cart Transform Functions Developer Guide
- Per-Character Pricing Setup
- Tier-Based Option Pricing
- Cart Transform vs Variant Pricing
Charge for options — free on Shopify
Print It My Way's Free plan includes Cart Transform pricing on 1 personalizer. All 5 patterns (flat, per-char, percentage, tiered, combo). No credit card.
Install Print It My Way