Product Series
Product Series Specification
Overview
The product series system provides RDB-like relationships for grouping product variants under a common series. This allows proper organization of related products while maintaining distinct product names for each variant.
Architecture
Data Files
src/data/product-master-data.mjs: Contains product catalog withseriesIdforeign keysrc/data/product-series.mjs: Contains series master data (series metadata)
Relationship Structure
graph LR
A[product-master-data.mjs]
B[product-series.mjs]
A1["zudo-stand-40<br/>seriesId: 'zudo-stand'"]
A2["zudo-stand-60<br/>seriesId: 'zudo-stand'"]
A3["zudo-stand-40x2<br/>seriesId: 'zudo-stand'"]
B1["'zudo-stand'<br/>name: 'zudo-stand'<br/>detailHref: '/notes/...'<br/>description: '...'"]
A1 -->|references| B1
A2 -->|references| B1
A3 -->|references| B1
style A1 fill:#2d3748,stroke:#4a5568,color:#fff
style A2 fill:#2d3748,stroke:#4a5568,color:#fff
style A3 fill:#2d3748,stroke:#4a5568,color:#fff
style B1 fill:#1a365d,stroke:#2c5282,color:#fff
Product Series Data Structure
Series Object Schema
Each series is defined as an object property, where the key is the series ID:
'series-id': {
name: 'Series Name', // Display name for the series
detailHref: '/notes/...', // URL to series introduction page
description: 'Brief description of the series'
}
Example: zudo-stand Series
'zudo-stand': {
name: 'zudo-stand',
detailHref: '/products/zudo-stand-intro/',
description: 'zudo-blockシリーズを支えるスタンド',
}
Product Data Integration
Adding seriesId to Products
In product-master-data.mjs:
{
slug: 'zudo-stand-40',
name: 'zudo-stand-40',
seriesId: 'zudo-stand', // References series in product-series.mjs
brand: 'takazudo',
// ... other fields
}
Field Distinction
name: Full product identifier (e.g., “zudo-stand-40”)subtitle: Optional secondary text (e.g., “Hybrid Oscillator”)seriesId: Optional reference to product series (e.g., “zudo-stand”)
Helper Functions
getSeriesById(seriesId)
Retrieves complete series metadata by ID.
import { getSeriesById } from '../data/product-series.mjs';
const series = getSeriesById('zudo-stand');
// Returns: { name: 'zudo-stand', detailHref: '/notes/...', description: '...' }
getSeriesName(seriesId)
Retrieves only the series name (useful for breadcrumbs).
import { getSeriesName } from '../data/product-series.mjs';
const name = getSeriesName('zudo-stand');
// Returns: 'zudo-stand'
seriesExists(seriesId)
Checks if a series exists.
import { seriesExists } from '../data/product-series.mjs';
if (seriesExists('zudo-stand')) {
// Series exists
}
Usage in Components
Breadcrumb Generation
The series system automatically resolves breadcrumbs in article-page-layout.tsx:
import { getSeriesName } from '../data/product-series.mjs';
// In BreadBlock component
if (seriesId) {
const seriesName = getSeriesName(seriesId);
breadcrumbText = seriesName || productName;
}
Result: Product detail pages for series members (e.g., zudo-stand-40) display the series name (“zudo-stand”) in breadcrumbs, not the individual variant name.
GraphQL Queries
Products with series metadata:
query {
allProduct {
nodes {
slug
name
subtitle
seriesId
# ... other fields
}
}
}
Adding a New Series
Step 1: Define Series Metadata
Add to product-series.mjs:
const productSeries = {
'my-new-series': {
name: 'My New Series',
detailHref: '/products/my-new-series-intro/',
description: 'Description of my new series',
},
// ... existing series
};
Step 2: Add seriesId to Products
Update products in product-master-data.mjs:
{
slug: 'my-new-series-variant-a',
name: 'my-new-series-variant-a',
seriesId: 'my-new-series',
// ... other fields
}
Step 3: Create Series Introduction Page
Create MDX file at the detailHref path with information about the series and its variants.
Behavior & Display
Product Listings
- Top page: Shows full product names (e.g., “zudo-stand-40”, “zudo-stand-60”)
- Brand pages: Shows full product names
- No changes to how product names appear in listings
Breadcrumbs
- With seriesId: Displays series name (e.g., “zudo-stand”)
- Without seriesId: Displays product name + subtitle
- No MDX overrides needed:
productNameBreadfrontmatter is no longer required for series products
Example Comparison
Before (without series system)
- Product:
name: "zudo-stand 40HP版" - Breadcrumb: Requires manual
productNameBread: "zudo-stand"in MDX frontmatter - Problem: Product name mixing with display text
After (with series system)
- Product:
name: "zudo-stand-40",seriesId: "zudo-stand" - Breadcrumb: Automatically resolves to “zudo-stand” from series metadata
- Benefit: Clean separation of data and presentation
Best Practices
When to Use Series
Use series grouping when:
- Multiple product variants belong to the same family
- Variants share the same introduction page
- Breadcrumbs should show the series name, not individual variant names
When NOT to Use Series
Do not use series when:
- Product is standalone with no variants
- Products are related but distinct (use
relatedProductIdsinstead) - Different product lines from same brand
Naming Conventions
- Series ID: Use kebab-case (e.g., “zudo-stand”, “oxi-one”)
- Series Name: Can use any format (e.g., “zudo-stand”, “OXI ONE”)
- Product Name: Full identifier with variant (e.g., “zudo-stand-40”, “oxi-one-mk2-black”)
TypeScript Types
export interface ProductSeries {
name: string;
detailHref: string;
description: string;
}
export interface Product {
slug: string;
name: string;
subtitle?: string | null;
seriesId?: string | null; // References ProductSeries by object key
// ... other fields
}
Related Documentation
- Product Master Data - Complete product catalog specification