Weaverse LogoWeaverse
All ArticlesPaul Phan
3 mins read

Shopify's New 16KB Metafield Limit: Migration Guide

Shopify's New 16KB Metafield Limit: Migration Guide Shopify announced a new 16KB limit for metafield writes starting API version 2026-04. Large metafields hurt storefront performance, and this change
#-hydrogen-#shopify#shopify-development
Shopify's New 16KB Metafield Limit: Migration Guide

Shopify's New 16KB Metafield Limit: Migration Guide

Shopify announced a new 16KB limit for metafield writes starting API version 2026-04. Large metafields hurt storefront performance, and this change enforces best practices.

Good news: Existing large metafields remain readable across all API versions. You won't lose data.

Bad news: If your app writes >16KB to a single metafield, it will break on API 2026-04+.

Who's Affected?

Check if your app stores:

  • Large JSON blobs in metafields
  • Base64-encoded images or files
  • Long-form content or HTML
  • Logs or analytics data

If you're storing more than 16KB in a single metafield, you need to migrate.

Migration Strategies

1. Use Metaobjects (Recommended)

Metaobjects are designed for complex structured data. Instead of stuffing everything in one metafield, use metaobject references.

// Before: Large metafield
product.metafields.custom.config = { huge: 'json blob' }
// After: Metaobject reference
const config = await createMetaobject({
type: 'product_config',
fields: { /* structured data */ }
})
product.metafields.custom.configRef = config.id

Benefits:

  • Better performance
  • More organized data structure
  • Easier to query and filter
  • No size limits

2. Split Across Multiple Metafields

Break large datasets into smaller chunks.

// Before: One large metafield
metafield.value = JSON.stringify(hugeObject) // >16KB
// After: Multiple smaller metafields
metafield_1.value = JSON.stringify(partOne)
metafield_2.value = JSON.stringify(partTwo)

3. Use the Files API

For large content (images, documents, long text), use the Files API and store the reference URL in a metafield.

4. Use List Metafields

If you're storing multiple values, Shopify's list metafields handle arrays natively without size bloat.

Timeline

  • Now: Audit your metafield usage
  • Before April 2026: Complete migration for any >16KB writes
  • API 2026-04+: Limit enforced

Quick Audit Script

Run this to find large metafields in your app:

curl -X POST https://your-store.myshopify.com/admin/api/2026-01/graphql.json \
-H "X-Shopify-Access-Token: $TOKEN" \
-d '{"query":"{ metafields(first: 100) { edges { node { namespace key value } } } }"}' \
| jq '.data.metafields.edges[] | select(.node.value | length > 16000)'

Additional Resources

Shopify has improved other data platform features to help:

  • Higher metaobject entry limits
  • Increased metafield definition limits per resource
  • Limits apply separately per app (not tied to shop limits)

For more guidance, see Data modeling with metafields and metaobjects.

Bottom Line

If you're not writing >16KB to single metafields, you're fine. If you are, migrate to metaobjects (best option) or split the data across multiple fields.

This is a performance optimization that benefits everyone. Plan your migration early.

Building with Hydrogen? Weaverse helps you ship faster with AI-powered theme development. Get started.

Reactions

Like
Love
Celebrate
Insightful
Cool!
Thinking

Join the Discussion

Continue Reading

More insights from the Weaverse team

Hydrogen 2026.1: What You Need to Know About the API Update

Hydrogen 2026.1: What You Need to Know About the API Update

Hydrogen 2026.1: What You Need to Know About the API Update Shopify released Hydrogen 2026.1.0, aligning with their quarterly API release cycle. Most changes are additive, but there's one breaking change that requires immediate attention if you have custom cart discount logic. The Breaking Change The cartDiscountCodesUpdate mutation now requires the discountCodes argument. Before (Implicit) cartDiscountCodesUpdate(cartId: $cartId) After (Explicit) cartDiscountCodesUpdate( cartId: $cartId discountCodes: [] # Required field ) What Else Changed Hydrogen 2026.1.0 updates both: Storefront API → 2026-01 Customer Account API → 2026-01 This is a quarterly version update aligned with Shopify's API release schedule. Action Items Update packages to Hydrogen 2026.1.0 Search codebase for cartDiscountCodesUpdate mutations Add explicit discountCodes argument to all calls Test cart discount functionality Official Changelogs For complete details, review: Storefront API 2026-01 Changelog Customer Account API 2026-01 Changelog Bottom Line 10-minute fix if you have custom discount code logic. Otherwise, smooth upgrade. The migration is straightforward but critical if your store uses programmatic discount code management. Don't skip testing cart functionality after the upgrade. Building with Hydrogen? Weaverse helps you ship faster with AI-powered theme development. Get started.

By Paul Phan
Read
Migrating from Legacy Customer Accounts in Shopify

Migrating from Legacy Customer Accounts in Shopify

For Theme Developers Remove Legacy Liquid Files Delete these files from your theme: templates/customers/login.liquid templates/customers/register.liquid templates/customers/account.liquid Use the New Web Component <!-- Replace legacy login forms with: --> <shopify-account></shopify-account> The shopify-account web component handles: Login/register flows Password reset Account management Multi-factor authentication For App Developers Use Customer Account UI Extensions If your app interacts with customer accounts, migrate to UI extensions: // extension.toml [[extensions.targeting]] target = "customer-account.profile.block-render" // React extension export default function ProfileBlock() { const { customer } = useApi() return ( <BlockStack> <Text>Loyalty Points: {customer.metafields.loyalty.points}</Text> </BlockStack> ) } Stats: 800+ apps already migrated. Don't get left behind. For Custom Storefronts (Hydrogen) Use Customer Account API import { useCustomerAccount } from '@shopify/hydrogen' export function AccountPage() { const { customer, accessToken, logout } = useCustomerAccount() if (!customer) { return <LoginButton /> } return ( <div> <h1>Welcome, {customer.firstName}</h1> <button onClick={logout}>Sign out</button> </div> ) } Migration Steps Update to Hydrogen 2026.x (includes Customer Account API) Replace legacy auth flows with useCustomerAccount hook Test OAuth flow with new customer accounts Remove legacy session handling code Why Migrate? Feature Legacy New MFA Manual Built-in Passwordless No Yes Single sign-on Limited Full OAuth Session security Basic Enhanced Mobile experience Poor Native Checklist Audit themes for legacy customer templates Replace forms with shopify-account component Update apps to use UI extensions Migrate Hydrogen to Customer Account API Test all auth flows Remove legacy session code Need help migrating? Weaverse can modernize your storefront in days, not months.

By Paul Phan
Read
React Router 7.13.1: URL Masking for Modal PDPs in Hydrogen

React Router 7.13.1: URL Masking for Modal PDPs in Hydrogen

React Router 7.13.1: URL Masking for Modal PDPs in Hydrogen The Problem You want a product quick-view modal that: Opens on top of your collection page Shows a shareable URL (/products/sneakers) Keeps the collection visible in the background Works with deep links and browser history Until now, this required manual backgroundLocation management—a clunky pattern that felt like fighting the framework. The Solution React Router 7.13.1 introduces unstable_mask on <Link>. First-class URL masking for Framework/Data Mode. // routes/collection.tsx export default function Collection({ loaderData }) { return ( <div className="collection-grid"> {loaderData.products.map((product) => ( <Link key={product.id} to={`/collection?product=${product.id}`} // Router state unstable_mask={`/products/${product.handle}`} // Browser URL > <ProductCard product={product} /> </Link> ))} {/* Modal overlay */} {loaderData.modalProduct && ( <dialog open> <ProductQuickView product={loaderData.modalProduct} /> </dialog> )} </div> ) } How It Works Router navigates to /collection?product=123 (internal state) Browser shows /products/sneakers (masked URL) Deep link /products/sneakers works normally (mask stripped on SSR) Back button closes modal, restores collection Detecting Masked State import { useLocation } from 'react-router' function ProductModal() { const location = useLocation() // Are we in a modal context? if (location.unstable_mask) { return <ModalProduct /> } // Direct navigation - full page return <FullProductPage /> } Hydrogen Use Cases Modal PDPs from collection grids Image galleries with shareable URLs Cart quick-edit from checkout Filter previews without navigation Important Notes Marked unstable - API may change before stabilization SPA only - Mask removed during SSR (deep links work normally) Requires React Router 7.13.1+ in your Hydrogen project Upgrade npm install react-router@latest

By Paul Phan
Read

Never miss an update

Subscribe to get the latest insights, tutorials, and best practices for building high-performance headless stores delivered to your inbox.

Join the community of developers building with Weaverse.