-
Notifications
You must be signed in to change notification settings - Fork 15
Description
Overview
The Weaverse SDK (@weaverse/hydrogen and related packages) currently does not provide SEO metadata capabilities for custom pages and other page types. While Shopify Hydrogen themes can leverage resource-based SEO (products, collections, etc.), custom pages created through Weaverse Builder lack metadata integration with theme implementations.
Problem Statement
- Missing metadata in HydrogenPageData: The current
HydrogenPageData
interface lacks SEO fields - WeaverseClient limitations:
loadPage()
method doesn't fetch or return SEO metadata - No theme integration patterns: Themes lack utilities to consume Weaverse-managed metadata
- React 19 compatibility gap: No support for modern React 19 native SEO components
- Legacy support needed: Projects not yet on React 19 need React Router meta function helpers
Proposed Solution
Phase 1: Core SDK Updates
HydrogenPageData Interface Extension (@weaverse/hydrogen
):
export interface HydrogenPageData extends WeaverseProjectDataType {
id: string
name: string
items: HydrogenComponentData[]
// NEW: SEO metadata support
seo?: {
title?: string
description?: string
keywords?: string
canonicalUrl?: string
openGraph?: {
title?: string
description?: string
image?: string
type?: string
}
twitter?: {
cardType?: string
title?: string
description?: string
image?: string
}
robots?: {
index?: boolean
follow?: boolean
}
}
}
WeaverseClient Updates:
class WeaverseClient {
async loadPage(params: LoadPageParams): Promise<HydrogenPageData> {
// Fetch page data including SEO metadata from Weaverse API
const response = await this.fetch(`/api/pages/${params.handle}?includeSeo=true`)
return {
...pageData,
seo: response.seo // Include SEO data in response
}
}
// NEW: Dedicated method for React Router meta functions
async getPageMeta(params: LoadPageParams): Promise<MetaDescriptor[]> {
const pageData = await this.loadPage(params)
return formatMetaDescriptors(pageData.seo)
}
}
Phase 2: React 19 Native Components
Weaverse SEO Component (@weaverse/react
):
interface WeaverseSEOProps {
pageData: HydrogenPageData
shopData?: ShopFragment
fallback?: SeoConfig
}
export function WeaverseSEO({ pageData, shopData, fallback }: WeaverseSEOProps) {
const seo = pageData.seo || fallback
return (
<>
<title>{seo?.title || pageData.name}</title>
<meta name="description" content={seo?.description} />
{seo?.keywords && <meta name="keywords" content={seo.keywords} />}
{seo?.canonicalUrl && <link rel="canonical" href={seo.canonicalUrl} />}
{/* Open Graph */}
{seo?.openGraph && (
<>
<meta property="og:title" content={seo.openGraph.title} />
<meta property="og:description" content={seo.openGraph.description} />
<meta property="og:image" content={seo.openGraph.image} />
<meta property="og:type" content={seo.openGraph.type || 'website'} />
</>
)}
{/* Twitter Cards */}
{seo?.twitter && (
<>
<meta name="twitter:card" content={seo.twitter.cardType || 'summary'} />
<meta name="twitter:title" content={seo.twitter.title} />
<meta name="twitter:description" content={seo.twitter.description} />
<meta name="twitter:image" content={seo.twitter.image} />
</>
)}
{/* Robots */}
<meta
name="robots"
content={`${seo?.robots?.index !== false ? 'index' : 'noindex'},${seo?.robots?.follow !== false ? 'follow' : 'nofollow'}`}
/>
</>
)
}
Phase 3: React Router Meta Function Helpers
Utility Functions (@weaverse/hydrogen
):
export function formatMetaFunction(seoData: PageSEOData): MetaDescriptor[] {
return [
{ title: seoData.title },
{ name: 'description', content: seoData.description },
{ name: 'keywords', content: seoData.keywords },
{ tagName: 'link', rel: 'canonical', href: seoData.canonicalUrl },
// OpenGraph tags
{ property: 'og:title', content: seoData.openGraph?.title },
{ property: 'og:description', content: seoData.openGraph?.description },
{ property: 'og:image', content: seoData.openGraph?.image },
{ property: 'og:type', content: seoData.openGraph?.type || 'website' },
// Twitter tags
{ name: 'twitter:card', content: seoData.twitter?.cardType || 'summary' },
{ name: 'twitter:title', content: seoData.twitter?.title },
{ name: 'twitter:description', content: seoData.twitter?.description },
{ name: 'twitter:image', content: seoData.twitter?.image },
// Robots
{
name: 'robots',
content: `${seoData.robots?.index !== false ? 'index' : 'noindex'},${seoData.robots?.follow !== false ? 'follow' : 'nofollow'}`
}
].filter(tag => tag.content) // Remove undefined values
}
export async function getWeaverseMeta(
context: AppLoadContext,
params: LoadPageParams
): Promise<MetaDescriptor[]> {
const pageData = await context.weaverse.loadPage(params)
return pageData.seo ? formatMetaFunction(pageData.seo) : []
}
Phase 4: Enhanced Integration Patterns
Shopify Integration (@weaverse/hydrogen
):
export function combineWeaverseAndShopifySEO(
weaverseSeo: PageSEOData,
shopifySeo: SeoConfig
): SeoConfig {
return {
title: weaverseSeo.title || shopifySeo.title,
description: weaverseSeo.description || shopifySeo.description,
// Merge and prioritize Weaverse data over Shopify defaults
...shopifySeo,
...weaverseSeo
}
}
Advanced Features:
- JSON-LD structured data support
- Multi-language SEO handling
- Dynamic meta tag injection
- SEO preview utilities for theme development
Theme Implementation Examples
React 19 Pattern (Recommended)
// app/routes/($locale).$.tsx - Custom pages
import { WeaverseSEO } from '@weaverse/react'
export default function CustomPage() {
const { weaverseData } = useLoaderData<typeof loader>()
return (
<>
<WeaverseSEO pageData={weaverseData} />
<WeaverseContent data={weaverseData} />
</>
)
}
export async function loader({ context, params }: LoaderFunctionArgs) {
const weaverseData = await context.weaverse.loadPage({
type: "CUSTOM",
handle: params["*"]
})
return { weaverseData }
}
React Router Meta Function Pattern (Legacy Support)
// app/routes/($locale).$.tsx - Custom pages
import { getWeaverseMeta } from '@weaverse/hydrogen'
export const meta: MetaFunction<typeof loader> = async ({ context, params }) => {
return await getWeaverseMeta(context, {
type: "CUSTOM",
handle: params["*"]
})
}
export async function loader({ context, params }: LoaderFunctionArgs) {
const weaverseData = await context.weaverse.loadPage({
type: "CUSTOM",
handle: params["*"]
})
return { weaverseData }
}
Hybrid Pattern (Best of Both)
// Support both patterns with adapter
import { MetaAdapter } from '@weaverse/hydrogen'
export const meta: MetaFunction<typeof loader> = ({ data }) => {
return data.metaDescriptors
}
export default function CustomPage() {
const { weaverseData, metaDescriptors } = useLoaderData<typeof loader>()
return (
<>
<MetaAdapter descriptors={metaDescriptors} />
<WeaverseContent data={weaverseData} />
</>
)
}
export async function loader({ context, params }: LoaderFunctionArgs) {
const weaverseData = await context.weaverse.loadPage({
type: "CUSTOM",
handle: params["*"]
})
const metaDescriptors = formatMetaFunction(weaverseData.seo || {})
return { weaverseData, metaDescriptors }
}
Package Updates Required
@weaverse/hydrogen
- Extend
HydrogenPageData
interface with SEO fields - Update
WeaverseClient.loadPage()
to fetch SEO data - Add
getPageMeta()
method for React Router compatibility - Add
formatMetaFunction()
utility - Add
getWeaverseMeta()
helper function - Add
combineWeaverseAndShopifySEO()
utility
@weaverse/react
- Create
WeaverseSEO
component for React 19 native meta - Add
MetaAdapter
component for backward compatibility - Add SEO-related TypeScript interfaces
- Add validation utilities for SEO data
@weaverse/schema
- Define
PageSEOData
interface - Add Zod schemas for SEO validation
- Export SEO-related types for theme consumption
@weaverse/core (if needed)
- Core SEO data structures
- Framework-agnostic utilities
Testing Requirements
-
Unit Tests
- SEO data fetching and formatting
- Meta tag generation accuracy
- Component rendering with different SEO configurations
-
Integration Tests
- WeaverseClient SEO data loading
- React 19 component meta tag hoisting
- React Router meta function integration
-
E2E Tests (Pilot Template)
- Custom page SEO rendering
- Meta tag presence in page source
- Social media preview functionality
Migration Strategy
For Existing Themes
- Immediate: Themes can opt-in to SEO features via SDK updates
- Backward Compatible: Existing routes continue working without SEO
- Gradual Migration: Themes can migrate route-by-route to React 19 patterns
- Documentation: Provide migration guides for both React 19 and React Router patterns
For New Themes
- Default SEO Support: New theme scaffolds include SEO best practices
- React 19 First: Encourage native meta component usage
- Complete Examples: Pilot template demonstrates all patterns
Dependencies
- Related Issue: Weaverse Builder UI for managing SEO metadata
- API Requirements: Builder API must provide SEO data endpoints
- React Compatibility: Support React 18+ and React 19
- Framework Support: React Router v7, potential future Next.js support
Acceptance Criteria
- HydrogenPageData includes optional SEO fields
- WeaverseClient fetches and returns SEO metadata
- React 19 WeaverseSEO component renders proper meta tags
- React Router helper functions generate correct MetaDescriptor arrays
- Backward compatibility maintained for existing themes
- Comprehensive TypeScript support
- Documentation with implementation examples
- Pilot template demonstrates all usage patterns
- Performance impact is minimal (lazy loading where appropriate)
Priority
High - This is essential for Weaverse themes to compete with other Shopify theme solutions that provide comprehensive SEO management capabilities.
Technical Notes
- Package Versions: Maintain semver compatibility across @weaverse/* packages
- Bundle Size: Keep SEO utilities tree-shakeable to minimize impact
- Framework Support: Design with future framework support in mind (Next.js, etc.)
- CDN Compatibility: Ensure meta tags work with CDN and edge caching
- Accessibility: Include proper semantic markup in meta tag generation
Metadata
Metadata
Assignees
Labels
Type
Projects
Status