From vercel-shop
Integrates Shopify metaobjects as CMS for homepage and marketing pages in shop templates. Adds GraphQL queries for cms_homepage and cms_page, transforming to domain types.
npx claudepluginhub vercel/shop --plugin vercel-shopThis skill uses the workspace's default tool permissions.
Add Shopify metaobject-based CMS support to the shop template. This replaces the hardcoded homepage content with Shopify-managed content using `cms_homepage` and `cms_page` metaobject definitions.
Guides defining Shopify Metafields and Metaobjects via TOML in shopify.app.toml for custom app data modeling, using GraphQL for values. For Metafields/Metaobjects prompts.
References Vercel Shop GraphQL patterns, fragments, cache conventions, and transforms for Shopify GraphQL queries, mutations, validation, and debugging in the template.
Create Shopify pages, blog posts, navigation, redirects, SEO metadata, and metaobjects via GraphQL/REST Admin API or browser automation.
Share bugs, ideas, or general feedback.
Add Shopify metaobject-based CMS support to the shop template. This replaces the hardcoded homepage content with Shopify-managed content using cms_homepage and cms_page metaobject definitions.
cms_homepage and cms_pagecms_homepage| Field handle | Type | Description |
|---|---|---|
title | single_line_text | Page title |
meta_title | single_line_text | SEO title override |
meta_description | single_line_text | SEO description override |
hero_headline | single_line_text | Hero banner headline |
hero_subheadline | single_line_text | Hero banner subheadline |
hero_image | file | Hero background image |
hero_cta_text | single_line_text | Hero call-to-action label |
hero_cta_link | single_line_text | Hero call-to-action URL |
sections | json | Array of content section definitions |
cms_page| Field handle | Type | Description |
|---|---|---|
slug | single_line_text | URL slug |
title | single_line_text | Page title |
locale | single_line_text | Locale code (e.g. en-US) |
meta_title | single_line_text | SEO title override |
meta_description | single_line_text | SEO description override |
hero_headline | single_line_text | Hero banner headline |
hero_subheadline | single_line_text | Hero banner subheadline |
hero_image | file | Hero background image |
hero_cta_text | single_line_text | Hero call-to-action label |
hero_cta_link | single_line_text | Hero call-to-action URL |
sections | json | Array of content section definitions |
lib/shopify/operations/cms.tsImplement three operations that return domain types from lib/types.ts:
import { shopifyFetch } from "@/lib/shopify/client";
import type { Homepage, MarketingPage } from "@/lib/types";
export async function getHomepage(locale: string): Promise<Homepage | null> {
"use cache: remote";
cacheLife("max");
cacheTag("cms-content");
// Query cms_homepage metaobject, transform to Homepage type
}
export async function getMarketingPage(
slug: string,
locale: string,
): Promise<MarketingPage | null> {
"use cache: remote";
cacheLife("max");
cacheTag("cms-content");
// Query cms_page metaobject by slug, transform to MarketingPage type
}
export async function getAllMarketingPageSlugs(): Promise<
Array<{ slug: string; updatedAt: string }>
> {
"use cache: remote";
cacheLife("max");
cacheTag("cms-content");
// Query all cms_page metaobjects, return slugs
}
Validate field names with shopify-ai-toolkit or vercel-shop:fetch-shopify-schema. Use metaobjects(type: "cms_homepage") and metaobjects(type: "cms_page") queries with @inContext locale directives.
Create lib/shopify/transforms/cms.ts to convert raw metaobject fields into the domain types:
sections JSON field into ContentSection[]ProductCard[] using getProductsByIdsHeroSectionMarketingImageUpdate app/page.tsx to use the CMS operation:
import { getHomepage } from "@/lib/shopify/operations/cms";
import { MarketingPageRenderer } from "@/components/cms/page-renderer";
export default async function HomePage() {
const locale = await getLocale();
const page = await getHomepage(locale);
if (!page) return <FallbackHomepage />;
return (
<Container>
<MarketingPageRenderer page={page} />
</Container>
);
}
Update app/pages/[slug]/page.tsx and app/sitemap.ts to use getMarketingPage and getAllMarketingPageSlugs.
Create app/api/webhooks/shopify-cms/route.ts:
import { updateTag } from "next/cache";
export async function POST(request: Request) {
// Verify Shopify webhook signature
updateTag("cms-content");
return new Response("OK");
}
lib/types.ts — Homepage, MarketingPage, ContentSection.ProductCard[] — components expect ready-to-render product data.alternates field on MarketingPage — this powers locale-aware URL switching.