20

BigCommerce Custom Fields and Metafields for SEO

Apr 19, 2026
24 min read


BigCommerce product pages default to three structured data fields: name, price, and availability. That’s it. Google’s Product schema spec lists over 40 recommended properties, and the ones that trigger rich results (brand, GTIN, MPN, color, material, aggregate ratings) are the exact ones BigCommerce themes leave empty. The data exists somewhere in your catalog. The platform just doesn’t pipe it into schema markup automatically.

Custom Fields and Metafields are the two mechanisms BigCommerce gives you for attaching extra data to products. They look similar on the surface, but they operate through completely different systems, serve different purposes, and interact with SEO in distinct ways. Custom Fields live in the admin panel and render on the storefront. Metafields live in the API layer and stay invisible until you write code to surface them. A proper BigCommerce SEO strategy uses both, each for what it does best.

This guide covers the full implementation: how to structure Custom Fields so Stencil templates can inject them into JSON-LD, how to use Metafields for data that shouldn’t show on the frontend, how to build dynamic FAQ schema per product, and how to leverage filterable Custom Fields for faceted navigation that actually helps organic rankings. Every code block here runs in production Stencil themes. We’ve built and tested these patterns across BigCommerce stores in industrial supply, fashion, electronics, and B2B wholesale through our BigCommerce development work.

Custom Fields vs. Metafields: Two Systems, Different SEO Roles

Custom Fields in BigCommerce are name-value pairs attached to a product through the admin panel (Products > [Product Name] > Custom Fields). Each Custom Field has a visible name and a visible value. The Stencil theme renders them on the product page by default, usually in a table below the product description. Shoppers see them. Googlebot sees them. They’re part of the rendered HTML.

Metafields are different. You create them through the BigCommerce Catalog API v3, specifically the /catalog/products/{product_id}/metafields endpoint. They store key-value data on the product, but the Stencil theme ignores them unless you write custom code to fetch and render them. They don’t appear in the admin’s product editor by default. They don’t render on the storefront. They exist purely in the API layer.

That distinction drives every SEO decision about which system to use for which data point.

When to Use Custom Fields

Custom Fields are the right choice when the data should be visible to shoppers AND consumed by structured data. Brand name, material composition, color, weight, dimensions, country of origin. These are product attributes customers want to see on the page. They also map directly to schema.org Product properties that Google uses for rich results. Putting “Material: 316 Stainless Steel” in a Custom Field accomplishes both goals with a single data entry.

Custom Fields also power storefront filtering. When you check the “Storefront filtering” box on a Custom Field, BigCommerce adds it as a facet option in category page navigation. That creates indexable URLs. More on that in the faceted navigation section.

When to Use Metafields

Metafields are the right choice when the data is for machines, not shoppers. Focus keywords for internal SEO tracking. Canonical URL overrides. Internal quality scores. FAQ pairs you want injected as schema but not rendered as visible HTML. Competitor price benchmarks. Supplier cost data. None of this belongs on the storefront, but all of it can drive SEO logic when your theme or middleware reads it from the API.

Metafields also handle data that exceeds Custom Field limitations. Custom Fields have a 255-character value limit. Metafield values can hold up to 65,535 characters. If you’re storing a block of JSON (like five FAQ question-answer pairs), Metafields are the only option.

Quick Reference Comparison

Feature Custom Fields Metafields
Created via Admin UI or API API only
Rendered on storefront Yes, by default No, requires custom code
Value character limit 255 characters 65,535 characters
Available in Stencil context Yes (product.custom_fields) No (requires API fetch)
Storefront filtering Yes (checkbox option) No
Namespace/scope control No Yes
Best SEO use Visible product attributes, schema properties Hidden SEO data, FAQ schema, canonical overrides

Using Custom Fields to Populate Product Structured Data

BigCommerce’s Cornerstone theme (and most third-party Stencil themes) outputs a basic Product JSON-LD block that includes name, description, image, sku, offers.price, and offers.availability. It does NOT include brand, gtin, mpn, material, color, weight, or aggregateRating. Google explicitly recommends all of these for Product rich result eligibility. Missing them doesn’t trigger a Search Console error. It just means your products won’t qualify for the enhanced SERP treatment that shows star ratings, price ranges, and availability badges.

The fix is straightforward. Add the missing data as Custom Fields on each product, then modify the Stencil theme to read those fields and inject them into the JSON-LD block. For a deeper walkthrough on the full Product schema implementation, see our BigCommerce structured data guide.

Step 1: Create the Custom Fields

In the BigCommerce admin, go to Products, open a product, and scroll to the Custom Fields section. Add the following fields (the exact names matter because your Handlebars template will match on them):

Custom Field Name Example Value Schema Property
brand CasterPro brand.name
gtin 0012345678901 gtin13
mpn CW6HDPU-2024 mpn
material Polyurethane material
color Industrial Gray color
weight 4.2 lbs weight

Note that BigCommerce has a native Brand field on products. However, the default Stencil schema template doesn’t always map it into the JSON-LD correctly. Using a Custom Field named “brand” gives you explicit control and avoids edge cases where the native Brand association gets dropped during theme rendering.

Step 2: Modify the Stencil Template to Read Custom Fields into JSON-LD

Open your theme’s product page template. In Cornerstone, the structured data block lives in templates/components/products/product-view.html or a dedicated schema partial. You’ll loop through product.custom_fields and conditionally inject each field into the JSON-LD object.

Here’s the Handlebars code for the complete schema block:

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Product",
  "name": "{{product.title}}",
  "image": "{{getImage product.main_image 'product_size'}}",
  "description": "{{strip_html product.description}}",
  "sku": "{{product.sku}}",
  {{#each product.custom_fields}}
    {{#if name '==' 'brand'}}
  "brand": {
    "@type": "Brand",
    "name": "{{value}}"
  },
    {{/if}}
    {{#if name '==' 'gtin'}}
  "gtin13": "{{value}}",
    {{/if}}
    {{#if name '==' 'mpn'}}
  "mpn": "{{value}}",
    {{/if}}
    {{#if name '==' 'material'}}
  "material": "{{value}}",
    {{/if}}
    {{#if name '==' 'color'}}
  "color": "{{value}}",
    {{/if}}
    {{#if name '==' 'weight'}}
  "weight": "{{value}}",
    {{/if}}
  {{/each}}
  "offers": {
    "@type": "Offer",
    "url": "{{product.url}}",
    "priceCurrency": "{{currency_selector.active_currency_code}}",
    "price": "{{product.price.without_tax.value}}",
    "availability": "{{#if product.can_purchase}}https://schema.org/InStock{{else}}https://schema.org/OutOfStock{{/if}}",
    "itemCondition": "https://schema.org/NewCondition"
  }
}
</script>

Handling the Trailing Comma Problem

JSON doesn’t allow trailing commas. The Handlebars loop above will produce a comma after the last Custom Field property, which breaks the JSON-LD. Stencil’s Handlebars implementation doesn’t have a native “last item” check in {{#each}} loops for Custom Fields.

Two solutions work reliably in production:

Option A: Add a dummy trailing property. Place a property like "additionalType": "https://schema.org/Product" as the last item in the JSON object, after the Custom Fields loop closes. This is valid schema and absorbs the trailing comma.

Option B: Build the schema in JavaScript. Instead of constructing JSON-LD in Handlebars, write a <script> block that builds a JavaScript object, pushes Custom Field values into it conditionally, then serializes it with JSON.stringify() and injects it into a script[type="application/ld+json"] element. This approach avoids comma issues entirely and gives you full programmatic control.

<script>
(function() {
  var schema = {
    "@context": "https://schema.org",
    "@type": "Product",
    "name": "{{product.title}}",
    "image": "{{getImage product.main_image 'product_size'}}",
    "description": "{{strip_html product.description}}",
    "sku": "{{product.sku}}",
    "offers": {
      "@type": "Offer",
      "url": "{{product.url}}",
      "priceCurrency": "{{currency_selector.active_currency_code}}",
      "price": "{{product.price.without_tax.value}}",
      "availability": "{{#if product.can_purchase}}https://schema.org/InStock{{else}}https://schema.org/OutOfStock{{/if}}",
      "itemCondition": "https://schema.org/NewCondition"
    }
  };

  {{#each product.custom_fields}}
    {{#if name '==' 'brand'}}
      schema.brand = {"@type": "Brand", "name": "{{value}}"};
    {{/if}}
    {{#if name '==' 'gtin'}}
      schema.gtin13 = "{{value}}";
    {{/if}}
    {{#if name '==' 'mpn'}}
      schema.mpn = "{{value}}";
    {{/if}}
    {{#if name '==' 'material'}}
      schema.material = "{{value}}";
    {{/if}}
    {{#if name '==' 'color'}}
      schema.color = "{{value}}";
    {{/if}}
    {{#if name '==' 'weight'}}
      schema.weight = "{{value}}";
    {{/if}}
  {{/each}}

  var el = document.createElement('script');
  el.type = 'application/ld+json';
  el.text = JSON.stringify(schema);
  document.head.appendChild(el);
})();
</script>

Option B is what we deploy on most stores. It’s more resilient, easier to debug, and lets you add conditional logic (like only including aggregateRating when the product has reviews) without fighting Handlebars syntax. For more on optimizing product pages alongside schema, check the BigCommerce product page SEO guide.

Metafields via the Catalog API for Programmatic SEO Data

The BigCommerce Catalog API v3 exposes a Metafields endpoint for every resource type: products, categories, brands, and variants. The product metafields endpoint is POST /catalog/products/{product_id}/metafields for creation and GET /catalog/products/{product_id}/metafields for retrieval.

Each metafield requires four properties: key, value, namespace, and permission_set. The namespace groups related metafields together (like “seo” or “faq”). The permission_set controls visibility: app_only restricts it to the creating app, read makes it readable by the storefront, and write makes it editable by other apps.

Creating a Metafield via the API

Here’s a cURL example that stores a focus keyword on a product:

curl --request POST \
  --url https://api.bigcommerce.com/stores/{store_hash}/v3/catalog/products/112/metafields \
  --header 'Content-Type: application/json' \
  --header 'X-Auth-Token: {access_token}' \
  --data '{
    "key": "focus_keyword",
    "value": "industrial caster wheel 6 inch",
    "namespace": "seo",
    "permission_set": "read",
    "description": "Primary SEO target keyword for this product"
  }'

And here’s one that stores a canonical URL override:

curl --request POST \
  --url https://api.bigcommerce.com/stores/{store_hash}/v3/catalog/products/112/metafields \
  --header 'Content-Type: application/json' \
  --header 'X-Auth-Token: {access_token}' \
  --data '{
    "key": "canonical_override",
    "value": "https://example.com/caster-wheels/heavy-duty-6-inch/",
    "namespace": "seo",
    "permission_set": "read",
    "description": "Override the default canonical URL for this product"
  }'

Reading Metafields on the Storefront

Metafields with permission_set: "read" or permission_set: "write" are accessible through the Storefront GraphQL API. This is the recommended approach for reading metafield data on the frontend without exposing your API credentials.

fetch('/graphql', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer {{settings.storefront_api.token}}'
  },
  body: JSON.stringify({
    query: `
      query ProductMetafields($productId: Int!) {
        site {
          product(entityId: $productId) {
            metafields(namespace: "seo", keys: ["focus_keyword", "canonical_override"]) {
              edges {
                node {
                  key
                  value
                }
              }
            }
          }
        }
      }
    `,
    variables: { productId: {{product.id}} }
  })
})
.then(res => res.json())
.then(data => {
  var metafields = data.data.site.product.metafields.edges;
  metafields.forEach(function(edge) {
    console.log(edge.node.key + ': ' + edge.node.value);
  });
});

The Storefront GraphQL API runs on the same domain as your store. No CORS issues. No API token exposure. The {{settings.storefront_api.token}} Handlebars variable in Stencil provides the token automatically.

SEO Data You Should Store as Metafields

Not all SEO data belongs on the visible product page. Metafields are the right storage mechanism for:

Dynamic FAQ Schema per Product Using Metafields

FAQ rich results expand your SERP listing with expandable question-answer pairs. Google shows them directly in search results, and they push competitor listings further down the page. On product pages, FAQ schema is one of the highest-impact structured data types you can add. The challenge on BigCommerce is that there’s no native FAQ feature on product pages. You have to build it.

The approach: store FAQ pairs as a JSON string in a product metafield, fetch them on page load via the Storefront GraphQL API, and inject both visible FAQ HTML and FAQPage JSON-LD into the page.

Step 1: Store FAQ Data as a Metafield

curl --request POST \
  --url https://api.bigcommerce.com/stores/{store_hash}/v3/catalog/products/112/metafields \
  --header 'Content-Type: application/json' \
  --header 'X-Auth-Token: {access_token}' \
  --data '{
    "key": "faq_pairs",
    "value": "[{\"question\":\"What weight capacity does this caster support?\",\"answer\":\"This 6-inch polyurethane caster is rated for 1,200 lbs per wheel. For heavier applications, see our 8-inch model rated at 2,000 lbs.\"},{\"question\":\"Can I use this caster on concrete floors?\",\"answer\":\"Yes. The polyurethane tread is specifically designed for hard surfaces including concrete, tile, and epoxy-coated floors. It will not mark or damage the surface.\"},{\"question\":\"Does this include the mounting plate?\",\"answer\":\"Yes. Each caster ships with a pre-attached 4.5 x 4 inch top plate with a standard 4-bolt hole pattern.\"}]",
    "namespace": "faq",
    "permission_set": "read",
    "description": "FAQ question-answer pairs for FAQPage schema"
  }'

The value is a JSON array serialized as a string. Each object has a question and answer key. You can store up to five or six FAQ pairs before approaching reasonable length limits.

Step 2: Fetch and Inject on Page Load

Add this script to your product page template. It fetches the FAQ metafield, parses the JSON, builds both the visible HTML and the JSON-LD schema, and injects both into the DOM.

<script>
(function() {
  var productId = {{product.id}};

  fetch('/graphql', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer {{settings.storefront_api.token}}'
    },
    body: JSON.stringify({
      query: `query {
        site {
          product(entityId: ${productId}) {
            metafields(namespace: "faq", keys: ["faq_pairs"]) {
              edges {
                node {
                  key
                  value
                }
              }
            }
          }
        }
      }`
    })
  })
  .then(function(res) { return res.json(); })
  .then(function(data) {
    var edges = data.data.site.product.metafields.edges;
    if (!edges.length) return;

    var faqPairs = JSON.parse(edges[0].node.value);
    if (!faqPairs.length) return;

    // Build visible FAQ section
    var faqHtml = '<div class="product-faq">';
    faqHtml += '<h3>Frequently Asked Questions</h3>';
    faqPairs.forEach(function(pair) {
      faqHtml += '<details><summary>' + pair.question + '</summary>';
      faqHtml += '<p>' + pair.answer + '</p></details>';
    });
    faqHtml += '</div>';

    var target = document.querySelector('.productView-description');
    if (target) {
      target.insertAdjacentHTML('afterend', faqHtml);
    }

    // Build FAQPage JSON-LD
    var faqSchema = {
      "@context": "https://schema.org",
      "@type": "FAQPage",
      "mainEntity": faqPairs.map(function(pair) {
        return {
          "@type": "Question",
          "name": pair.question,
          "acceptedAnswer": {
            "@type": "Answer",
            "text": pair.answer
          }
        };
      })
    };

    var schemaEl = document.createElement('script');
    schemaEl.type = 'application/ld+json';
    schemaEl.text = JSON.stringify(faqSchema);
    document.head.appendChild(schemaEl);
  });
})();
</script>

Googlebot and JavaScript-Rendered Schema

Google’s crawler renders JavaScript. It runs your fetch calls, waits for the DOM to update, and indexes the result. FAQ schema injected via JavaScript works. Google has confirmed this repeatedly, and we’ve verified it across multiple stores by checking the “URL Inspection” tool in Search Console after deploying this exact pattern. The rendered HTML shows the JSON-LD block in the head, and Google picks it up within one to three days of crawling.

One caveat: the Storefront GraphQL API call adds latency before the schema appears in the DOM. Googlebot has a rendering budget. If your page takes too long to fully render, Google might index the pre-JavaScript version. Keep the GraphQL query minimal (single namespace, specific keys) to ensure fast resolution. Avoid chaining multiple API calls. A single fetch that returns the FAQ data is all you need.

Filterable Attributes for Faceted Navigation SEO

BigCommerce supports native faceted navigation on category pages. Products filter by price, brand, rating, and any Custom Field you’ve enabled for storefront filtering. That last part is where the SEO opportunity lives. Custom Fields with the “Storefront filtering” checkbox checked become filter facets that create new URL combinations in your category navigation.

BigCommerce natively supports filtering by brand, price, and rating. It does NOT natively support filtering by material, pattern, season, certification level, thread count, or any other product attribute specific to your catalog. Custom Fields fill that gap.

Enabling a Custom Field for Filtering

Go to Products > Product Filtering in your BigCommerce admin. You’ll see a list of all Custom Fields that exist across your catalog. Toggle on the ones you want shoppers to filter by. The field must exist on enough products to be useful. BigCommerce won’t display a filter option on a category page unless multiple products in that category have the field.

Once enabled, visiting a category page like /industrial-casters/ will show a new filter option for each enabled Custom Field. Clicking “Material: Polyurethane” creates a URL like /industrial-casters/?Material=Polyurethane. That URL is indexable by default.

The SEO Opportunity

Every filter combination creates a new URL that targets a more specific query. The base category /industrial-casters/ targets “industrial casters.” The filtered URL /industrial-casters/?Material=Polyurethane targets “polyurethane industrial casters.” If you’ve enabled Material and Weight Class as filters, a shopper (or Googlebot) can reach /industrial-casters/?Material=Polyurethane&Weight+Class=Heavy+Duty, which targets “heavy duty polyurethane industrial casters.” These long-tail combinations convert at higher rates because buyer intent is more specific.

The challenge is controlling which combinations get indexed. Unlimited facet combinations create exponential URL counts. A category with 5 materials, 4 weight classes, and 6 colors produces 120 possible filter combinations. Most of those pages contain too few products to provide value. Google might see them as thin content or burn crawl budget indexing pages with two products on them.

Canonical Strategy for Filtered Pages

Use canonical tags to control indexing. For the detailed implementation of canonical tag management on BigCommerce category pages, including filter parameter handling, see our BigCommerce category page canonicals guide.

The short version: set the canonical on filtered pages to the base category URL by default. Only override the canonical to the filtered URL when the filtered page has (1) enough products to not be thin content (we use 6 as the minimum), (2) search volume for the filtered query, and (3) unique meta title and description.

You implement this in Stencil by modifying the category template’s canonical tag logic. The {{#if category.url}} block in the theme’s <head> section needs conditional logic that checks whether the current filter combination should self-canonicalize or point back to the parent category.

{{!-- In templates/pages/category.html or the head partial --}}
{{#if category.faceted_search_enabled}}
  {{#each category.active_filters}}
    {{!-- Check if this filter combination has a custom canonical --}}
  {{/each}}
  <link rel="canonical" href="{{category.url}}">
{{else}}
  <link rel="canonical" href="{{category.url}}">
{{/if}}

For production deployments, we typically build a metafield-based system where specific filter combinations are flagged for self-canonicalization. The category page reads a metafield containing an allow-list of filter parameter combinations that should be indexed.

Bulk Custom Field Management via CSV Import

Adding Custom Fields one product at a time through the admin panel doesn’t scale. A catalog with 500 products and 6 Custom Fields per product means 3,000 individual field entries. BigCommerce’s CSV import handles this in a single upload.

CSV Format for Custom Fields

BigCommerce’s product import CSV accepts Custom Fields using a specific column naming convention. Each Custom Field gets its own column with the header format Custom Field: [Field Name]. Here’s a minimal example:

Product ID,Product Name,Custom Field: brand,Custom Field: gtin,Custom Field: mpn,Custom Field: material,Custom Field: color
112,"Heavy-Duty Caster Wheel 6-Inch","CasterPro","0012345678901","CW6HDPU-2024","Polyurethane","Industrial Gray"
113,"Standard Caster Wheel 4-Inch","CasterPro","0012345678902","CW4STD-2024","Rubber","Black"
114,"Locking Swivel Caster 5-Inch","RollerMax","0012345678903","LS5SWV-2024","Nylon","White"

Upload this CSV through Products > Import. BigCommerce matches products by Product ID (or SKU, depending on your import settings) and creates or updates the Custom Fields on each product.

Practical Tips for Bulk Import

Export your current product catalog first. BigCommerce’s export includes existing Custom Fields, so you can see the current state and add new columns without overwriting what’s already there. Always test with a small batch (5 to 10 products) before running the full import. A malformed CSV can blank out existing Custom Fields if the column is present but empty.

For stores with thousands of SKUs, the API batch approach is faster and more reliable than CSV. The PUT /catalog/products/{id} endpoint accepts a custom_fields array. You can script the entire update in Python or Node.js and push all fields programmatically. Rate limits on the BigCommerce API v3 are generous (450 requests per 30-second window for most plans), so a catalog of 5,000 products takes under 10 minutes to process.

import requests
import csv
import time

API_URL = "https://api.bigcommerce.com/stores/{store_hash}/v3/catalog/products"
HEADERS = {
    "X-Auth-Token": "{access_token}",
    "Content-Type": "application/json"
}

with open("custom_fields.csv", "r") as f:
    reader = csv.DictReader(f)
    for row in reader:
        product_id = row["Product ID"]
        custom_fields = []

        for key in ["brand", "gtin", "mpn", "material", "color"]:
            if row.get(f"Custom Field: {key}"):
                custom_fields.append({
                    "name": key,
                    "value": row[f"Custom Field: {key}"]
                })

        if custom_fields:
            resp = requests.put(
                f"{API_URL}/{product_id}",
                headers=HEADERS,
                json={"custom_fields": custom_fields}
            )
            if resp.status_code == 200:
                print(f"Updated product {product_id}")
            else:
                print(f"Error on product {product_id}: {resp.status_code}")

        time.sleep(0.07)  # Stay within rate limits

Custom Field Naming Conventions for SEO

Custom Field names are visible to shoppers on the product page. They also serve as the identifiers your Stencil templates match against when injecting values into structured data. A sloppy naming convention creates problems in both places.

Rules That Prevent Headaches

Lowercase, single-word names for schema-mapped fields. Use brand, not Brand Name or BRAND. Your Handlebars template matches on the exact string. Inconsistent casing across products means the template match fails silently. A product with “Brand” instead of “brand” won’t get the brand property in its schema. Stencil’s Handlebars comparison is case-sensitive.

Use hyphens for multi-word display names. If the field needs to show “Thread Count” to shoppers, name it thread-count and use CSS or a Handlebars helper to format the display. Better yet, use thread_count with underscores, which maps cleanly to API field naming conventions and avoids URL encoding issues in filter parameters.

Prefix non-schema fields with an underscore. Fields that exist purely for filtering or internal use (not mapped to schema.org properties) should start with an underscore: _season, _certification_level, _warehouse_location. This makes it immediately obvious in the admin and in template code which fields feed structured data and which serve other purposes.

Document the conventions. Create a spreadsheet or internal wiki page that lists every Custom Field name your store uses, its expected value format, whether it maps to a schema property, and whether storefront filtering is enabled. Every person who touches the product catalog needs to reference this document. One misspelled field name breaks the schema for that product, and you won’t catch it until the next structured data audit.

Value Formatting for Schema Compliance

Schema.org has specific expectations for certain property values. The gtin13 field must be exactly 13 digits. The weight field should include a unit (Google accepts “4.2 lbs” or structured QuantitativeValue markup). The color field should use recognizable color names, not internal codes. “Industrial Gray” is fine. “IG-7420” is not.

Validate Custom Field values at the point of entry. If your catalog team uses a spreadsheet to manage product data before import, add data validation rules to the spreadsheet columns. GTIN columns should enforce 13-digit numeric format. Color columns should reference a predefined list. This catches errors before they reach BigCommerce, where they’d silently produce invalid schema markup.

Metafield Namespaces and Permission Scopes

Every metafield in BigCommerce belongs to a namespace. The namespace is a string you define when creating the metafield. It groups related metafields together and prevents key collisions between different apps or systems writing to the same product.

Namespace Strategy

Use descriptive, purpose-driven namespaces. We use these across client stores:

Avoid generic namespaces like custom or data. When you’re debugging why a particular product’s schema looks wrong six months from now, namespace specificity saves hours.

Permission Scopes Explained

BigCommerce metafields support three permission scopes:

app_only: Only the API client that created the metafield can read or write it. Other API clients and the storefront GraphQL API cannot see it. Use this for sensitive internal data: supplier costs, margin calculations, competitor pricing.

read: Any API client can read it. The Storefront GraphQL API can read it. The creating app retains write access. Use this for data the frontend needs to consume: FAQ pairs, schema overrides, canonical URLs.

write: Any API client can read and write it. The Storefront GraphQL API can read it. Use this sparingly. Open write access means any app connected to your store can modify the metafield. In practice, read handles most SEO use cases.

Getting the permission scope wrong is one of the most common metafield issues we debug on client stores. The symptom is always the same: the GraphQL query returns an empty edges array even though you know the metafield exists. Nine times out of ten, the metafield was created with app_only permission and the storefront can’t see it. The fix is to delete and recreate the metafield with read scope (you can’t change the permission_set on an existing metafield through the API).

Performance Impact of API-Loaded Metafields

Fetching metafields via the Storefront GraphQL API adds a network request to your product page load. That request has performance implications you need to measure and manage.

Latency Numbers

In our testing across 15 BigCommerce stores, the Storefront GraphQL API responds in 80 to 250 milliseconds for a simple metafield query (single namespace, one to three keys). That’s the server response time. Total client-side execution including DNS resolution, TLS negotiation (on first request), and JSON parsing adds another 50 to 100 milliseconds. So the full round trip costs between 130 and 350 milliseconds on a typical connection.

For a product page that already takes 1.5 to 2.5 seconds to reach Largest Contentful Paint, adding 200 milliseconds for a metafield fetch is noticeable but manageable. The FAQ section renders below the fold on most layouts, so it doesn’t affect LCP. It will, however, show up in Total Blocking Time if the JavaScript execution blocks the main thread.

Mitigation Strategies

Load metafields asynchronously. The fetch call in the FAQ example above is already async, but make sure the .then() callback doesn’t trigger a layout reflow on above-the-fold content. Insert the FAQ HTML into a container that already has a reserved height, or place it below the fold where the reflow won’t shift visible elements.

Use a single GraphQL query for all metafield namespaces. If a product page needs FAQ data from the faq namespace and canonical override from the seo namespace, combine them into one query. Two separate fetch calls double the latency.

query {
  site {
    product(entityId: 112) {
      faqMeta: metafields(namespace: "faq") {
        edges { node { key value } }
      }
      seoMeta: metafields(namespace: "seo") {
        edges { node { key value } }
      }
    }
  }
}

Cache the response on the client side. Store the GraphQL response in sessionStorage keyed by product ID. On subsequent visits to the same product page (common during comparison shopping), skip the API call and read from cache. Metafield data changes infrequently. A session-scoped cache won’t serve stale data across visits.

<script>
(function() {
  var productId = {{product.id}};
  var cacheKey = 'metafields_' + productId;
  var cached = sessionStorage.getItem(cacheKey);

  if (cached) {
    processMetafields(JSON.parse(cached));
    return;
  }

  fetch('/graphql', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer {{settings.storefront_api.token}}'
    },
    body: JSON.stringify({
      query: `query {
        site {
          product(entityId: ${productId}) {
            metafields(namespace: "faq", keys: ["faq_pairs"]) {
              edges { node { key value } }
            }
          }
        }
      }`
    })
  })
  .then(function(res) { return res.json(); })
  .then(function(data) {
    sessionStorage.setItem(cacheKey, JSON.stringify(data));
    processMetafields(data);
  });

  function processMetafields(data) {
    // FAQ injection logic here
  }
})();
</script>

Preconnect to the GraphQL endpoint. Add <link rel="preconnect" href="/"> in the document head. Since the Storefront GraphQL API runs on the same origin, this isn’t strictly necessary, but including a preload hint for the GraphQL endpoint itself can shave 20 to 30 milliseconds off the connection setup on slower devices.

When to Skip the API Call Entirely

If you only use metafields for FAQ schema and not every product has FAQ data, add a sentinel Custom Field named _has_faq with a value of true on products that have FAQ metafields. Check this Custom Field in Stencil before making the GraphQL call. Products without the sentinel field skip the API request entirely.

{{#each product.custom_fields}}
  {{#if name '==' '_has_faq'}}
    <script>window.__loadFaqSchema = true;</script>
  {{/if}}
{{/each}}

<script>
if (window.__loadFaqSchema) {
  // Run the GraphQL fetch and FAQ injection
}
</script>

This pattern eliminates unnecessary API calls on products that don’t have FAQ data, keeping those pages fast while still enabling dynamic schema injection where it exists.

Frequently Asked Questions

What is the difference between Custom Fields and Metafields in BigCommerce?

Custom Fields are name-value pairs you create in the BigCommerce admin (or via API) that render on the product page storefront by default. Shoppers see them. Metafields are API-only data attached to products that don’t appear on the storefront unless you write custom code to fetch and display them. Custom Fields are limited to 255 characters per value. Metafields support up to 65,535 characters. Custom Fields can be enabled for storefront filtering. Metafields cannot. Use Custom Fields for visible product attributes and schema properties. Use Metafields for hidden SEO data, FAQ content, and internal tracking fields.

Can Google read structured data injected via JavaScript on BigCommerce?

Yes. Google’s crawler renders JavaScript and indexes the resulting DOM. JSON-LD injected via a JavaScript fetch call to BigCommerce’s Storefront GraphQL API will be picked up by Google during its rendering pass. We’ve confirmed this across multiple BigCommerce stores using Google Search Console’s URL Inspection tool, which shows the rendered HTML including dynamically injected schema. The key constraint is that Google has a rendering budget per page, so keep your API calls minimal and fast to ensure the schema is present when Google captures the rendered output.

How many Custom Fields can I add per product in BigCommerce?

BigCommerce allows up to 250 Custom Fields per product. In practice, most stores use between 5 and 15 fields per product. Each field has a name limit of 250 characters and a value limit of 255 characters. For schema-mapped fields (brand, gtin, mpn, material, color, weight), you’ll typically use 6 to 8 Custom Fields. Add storefront filtering fields and internal flags, and most catalogs land in the 10 to 20 range per product.

Do BigCommerce metafields affect page load speed?

Metafields themselves don’t affect speed because they aren’t loaded by default. The Storefront GraphQL API call you make to fetch them adds 130 to 350 milliseconds of latency depending on query complexity and connection speed. This impacts Total Blocking Time but usually not Largest Contentful Paint, because metafield-dependent content (like FAQ sections) renders below the fold. Mitigate the impact by combining multiple namespace queries into a single GraphQL request, caching responses in sessionStorage, and using a sentinel Custom Field to skip the API call on products that don’t have metafield data.

Can I use Custom Fields to improve BigCommerce category page SEO?

Yes. Custom Fields with storefront filtering enabled become faceted navigation options on category pages. Each filter combination creates an indexable URL that targets more specific search queries. A category page for “industrial casters” with a Material filter creates URLs like /industrial-casters/?Material=Polyurethane that target “polyurethane industrial casters.” Control which filter combinations get indexed using canonical tags. Point low-value combinations back to the base category URL, and let high-value combinations (with sufficient products and search volume) self-canonicalize. This is covered in detail in our BigCommerce category page canonicals guide.

Ready to automate your marketing?

Deploy 7 AI agents per client. Research, strategy, content, SEO, and sales on autopilot.

Get Started
FK
Faris Khalil
Founder and lead developer at Digital Roxy. Builds custom e-commerce stores on Shopify, WordPress, and BigCommerce. Specializes in platform migrations, headless architecture, and AI-driven marketing systems for agencies.
Scroll to Top