- Custom Fields vs. Metafields: Two Systems, Different SEO Roles
- Using Custom Fields to Populate Product Structured Data
- Metafields via the Catalog API for Programmatic SEO Data
- Dynamic FAQ Schema per Product Using Metafields
- Filterable Attributes for Faceted Navigation SEO
- Bulk Custom Field Management via CSV Import
- Custom Field Naming Conventions for SEO
- Metafield Namespaces and Permission Scopes
- Performance Impact of API-Loaded Metafields
- Frequently Asked Questions
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:
- Focus keyword. The primary search query this product page targets. Your internal SEO team references this during content audits. It doesn’t belong in the page’s visible HTML.
- Canonical URL override. When a product exists in multiple categories and you need to force a specific canonical that differs from BigCommerce’s default.
- Internal quality score. A numeric score (1 to 10) your team assigns based on content completeness, image quality, and review count. Useful for prioritizing which product pages need optimization work.
- FAQ pairs. Question-and-answer content injected as FAQPage schema without cluttering the product page layout.
- Hreflang overrides. When a product maps to a different URL on a regional storefront and the automatic hreflang mapping doesn’t handle it correctly.
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:
seofor focus keywords, canonical overrides, meta title templates, and indexing directives.faqfor FAQ question-answer pairs used in FAQPage schema.schemafor structured data overrides that don’t map to standard Custom Fields (e.g., a customadditionalPropertyarray for product specs).internalfor quality scores, content audit flags, and last-optimized timestamps. These never touch the storefront.
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.