VelaFront / headless
The SEO Suite is headless-first. Every storefront feature has a GraphQL counterpart, exposed on the standard Magento types so VelaFront, PWA Studio, Hyvä Checkout, or any custom Next.js storefront can consume the same data the rendered Luma page sees.
The seo: SeoMetadata field
Available on:
ProductInterfaceCategoryInterfaceCmsPageCmsPageItem
Returns:
type SeoMetadata {
title: String
description: String
robots: String
canonical: String
open_graph: [SeoTag!]!
structured_data: [String!]! # JSON-LD docs as raw JSON strings
}
type SeoTag {
name: String! # e.g. "og:title", "twitter:card"
content: String!
}
Full schema reference: GraphQL → SEO Metadata.
Example product query
query Product($urlKey: String!) {
products(filter: { url_key: { eq: $urlKey } }) {
items {
sku
name
seo {
title
description
robots
canonical
open_graph { name content }
structured_data
}
}
}
}
Rendering in Next.js (App Router)
// app/products/[urlKey]/page.tsx
import type { Metadata } from 'next';
export async function generateMetadata({ params }): Promise<Metadata> {
const { products } = await graphqlFetch(PRODUCT_QUERY, { urlKey: params.urlKey });
const seo = products.items[0]?.seo;
if (!seo) return {};
const ogTags = Object.fromEntries(seo.open_graph.map(t => [t.name, t.content]));
return {
title: seo.title,
description: seo.description,
robots: seo.robots,
alternates: { canonical: seo.canonical },
openGraph: {
title: ogTags['og:title'],
description: ogTags['og:description'],
url: ogTags['og:url'],
images: ogTags['og:image'] ? [{ url: ogTags['og:image'] }] : [],
type: ogTags['og:type'] as 'website' | 'product',
siteName: ogTags['og:site_name'],
},
twitter: {
card: ogTags['twitter:card'] as 'summary' | 'summary_large_image',
title: ogTags['twitter:title'],
description: ogTags['twitter:description'],
images: ogTags['twitter:image'] ? [ogTags['twitter:image']] : [],
site: ogTags['twitter:site'],
},
};
}
export default function ProductPage({ /* ... */ }) {
const seo = /* same query as above, or pass through from parent */;
return (
<>
{seo.structured_data.map((json, i) => (
<script
key={i}
type="application/ld+json"
// SEO Suite already escapes </ — JSON output is safe to inject as-is
dangerouslySetInnerHTML={{ __html: json }}
/>
))}
{/* …product UI… */}
</>
);
}
What about the storefront templates?
When you run headless, Magento's Luma/Hyvä storefront isn't rendered — so the four head.additional templates are irrelevant. The GraphQL field is the canonical surface.
Caching
The GraphQL response participates in Magento's standard GraphQL cache (Varnish / FPC tag-based invalidation). Tags fired include:
cat_p_<product_id>— product changes invalidatecat_c_<category_id>— category changes invalidatecms_p_<page_id>— CMS-page changes invalidatecfg— config changes (e.g. flipping JSON-LD off) invalidate
So the response is cached aggressively but invalidated cleanly when admin edits anything material.
Existing v1 GraphQL fields preserved
meta_robots: String is still available on CategoryInterface, CmsPage, and CmsPageItem for backward compatibility — the new seo field is additive.
Limitations
- Hreflang links are not yet exposed via GraphQL — the storefront emits them, but the headless surface does not (yet). Roadmapped for v2.9.
- URL-relationship overrides (manual canonical/alternate mappings in
byte8_seosuite_url) are surfaced throughcanonicalfor CMS pages but not for products/categories where Magento's built-in helper drives the value.
Next
- GraphQL → SEO Metadata — full schema docs
- GraphQL → Examples — copy-pasteable queries for category, CMS, product