
Ecommerce Seo
- 114 installs
- 41 repo stars
- Updated March 13, 2026
- finsilabs/awesome-ecommerce-skills
Improve organic search with optimized product meta tags, JSON-LD structured data for Google Shopping, and XML sitemaps.
About
Covers technical and content SEO foundations for product pages: meta tags, structured data, canonicals, sitemaps, and Core Web Vitals. A developer uses it to rank products in Google Shopping or diagnose missing rich results.
- Per-platform table of what SEO features are automatic
- Focus on title/description and structured-data quality over plumbing
Ecommerce Seo by the numbers
- 114 all-time installs (skills.sh)
- Ranked #1,112 of 1,879 Marketing & SEO skills by installs in the Skillselion catalog
- Data as of Aug 3, 2026 (Skillselion catalog sync)
npx skills add https://github.com/finsilabs/awesome-ecommerce-skills --skill ecommerce-seoAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 114 |
|---|---|
| repo stars | ★ 41 |
| Last updated | March 13, 2026 |
| Repository | finsilabs/awesome-ecommerce-skills ↗ |
What it does
Improve organic search with optimized product meta tags, JSON-LD structured data for Google Shopping, and XML sitemaps.
Files
E-commerce SEO
Overview
E-commerce SEO covers the technical and content foundations that help search engines understand and rank your product pages: meta tags, structured data (JSON-LD), canonical URLs, XML sitemaps, and Core Web Vitals. Shopify and WooCommerce handle most technical SEO automatically; your effort should focus on optimizing titles, descriptions, and structured data quality — not plumbing.
When to Use This Skill
- When building product pages that need to rank in Google Shopping and organic search
- When implementing JSON-LD structured data for rich snippets (price, availability, reviews)
- When handling canonical URLs for products with multiple variants or filter combinations
- When generating XML sitemaps for a large catalog (10K+ products)
- When diagnosing why products are not appearing in Google Shopping rich results
Core Instructions
Step 1: Check what your platform handles automatically
Before installing anything, understand what is already built in:
| Feature | Shopify | WooCommerce | BigCommerce |
|---|---|---|---|
| XML sitemap | Auto-generated at /sitemap.xml | Auto-generated at /sitemap_index.xml (with Yoast SEO) | Auto-generated at /xmlsitemap.xml |
| Canonical URLs | Yes (built-in) | Yes (with Yoast SEO) | Yes (built-in) |
| Meta title/description editing | Yes (via product editor) | Yes (via Yoast SEO fields) | Yes (via product editor) |
| JSON-LD product schema | Basic (varies by theme) | Requires WooCommerce + Yoast or Rank Math | Basic (varies by theme) |
| robots.txt | Editable in Online Store settings | Editable via file or Yoast | Editable via admin |
Step 2: Install an SEO foundation
---
Shopify
Shopify handles sitemaps, canonicals, and basic schema automatically. Focus on:
1. Install a SEO app for enhanced structured data and bulk editing:
- Yoast SEO for Shopify ($19/mo) — most comprehensive
- Schema Plus (free tier) — focused on JSON-LD structured data
- SEO Manager ($20/mo) — bulk meta editing + structured data
2. Go to Shopify Admin → Online Store → Preferences and verify:
- Your homepage title and meta description are set
- Google Analytics is connected
3. Go to each product's page in admin and fill in the SEO section at the bottom:
- Write unique meta titles following:
[Brand] [Product Name] - [Key Attribute] | [Store Name] - Write unique meta descriptions (150–160 characters) that include price range, key benefit, and a call to action
4. Submit your sitemap to Google Search Console: go to search.google.com/search-console → Sitemaps → Add yourstore.com/sitemap.xml
---
WooCommerce
1. Install Yoast SEO (free) or Rank Math (free) from the WordPress plugin directory — these are required for proper WooCommerce SEO 2. After installing Yoast SEO:
- Go to Yoast SEO → Search Appearance → WooCommerce and configure product page templates
- Enable JSON-LD structured data under Yoast SEO → Search Appearance → Schema
- Go to Yoast SEO → Tools → Bulk Editor to edit meta titles and descriptions for all products at once
3. For enhanced product schema (price, availability, reviews):
- Install Rank Math which includes WooCommerce Product Schema with review aggregation built in
4. Submit sitemap to Google Search Console: Yoast auto-generates /sitemap_index.xml
---
BigCommerce
1. BigCommerce includes basic SEO features built-in. Go to BigCommerce Admin → Products → [Edit Product] → SEO tab 2. For enhanced structured data: install SEO Expert from the BigCommerce App Marketplace 3. Go to Store Setup → Search Engine Optimization to configure global defaults 4. Submit your sitemap at yourstore.com/xmlsitemap.xml to Google Search Console
---
Custom / Headless
For headless storefronts, implement structured data manually. Serve JSON-LD on every product page:
function buildProductJsonLd(product: Product, reviews: ReviewSummary) {
return {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.title,
image: product.images.map(img => img.src),
description: product.metaDescription || product.description.slice(0, 200),
sku: product.variants[0]?.sku,
brand: { '@type': 'Brand', name: product.vendor },
offers: product.variants.length === 1
? {
'@type': 'Offer',
url: `https://yourstore.com/products/${product.slug}`,
priceCurrency: 'USD',
price: (product.variants[0].priceInCents / 100).toFixed(2),
availability: product.variants[0].inventoryQuantity > 0
? 'https://schema.org/InStock'
: 'https://schema.org/OutOfStock',
}
: {
'@type': 'AggregateOffer',
lowPrice: (Math.min(...product.variants.map(v => v.priceInCents)) / 100).toFixed(2),
highPrice: (Math.max(...product.variants.map(v => v.priceInCents)) / 100).toFixed(2),
priceCurrency: 'USD',
offerCount: product.variants.length,
},
...(reviews.count > 0 ? {
aggregateRating: {
'@type': 'AggregateRating',
ratingValue: reviews.average.toFixed(1),
reviewCount: reviews.count,
bestRating: '5',
},
} : {}),
};
}For canonical URL handling on variant pages — strip variant parameters:
// Always use the base product URL as canonical
// /products/blue-widget?variant=123 → canonical: /products/blue-widget
function getCanonicalUrl(path: string): string {
// Strip variant query parameters
return `https://yourstore.com${path.split('?')[0]}`;
}For large catalogs (10k+ products), use a sitemap index:
// Serve /sitemap.xml as a sitemap index pointing to paginated product sitemaps
// Each child sitemap: max 50,000 URLs
// Regenerate every 6 hours or on product publish/unpublish eventsStep 3: Optimize product titles and descriptions
This is the highest-ROI SEO work. Follow these title formats:
- Product title format:
[Brand] [Product Name] [Key Attribute] - [Store Name] - Example: "Nike Air Max 90 White - Running Store"
- Meta description format: Include price range, key benefit, and CTA in 150–160 characters
- Example: "Shop Nike Air Max 90 from $120. Lightweight cushioning for daily runs. Free shipping on orders over $75. Shop now."
Common issues to fix:
- Duplicate meta titles across variants (fix: add variant-specific attributes to the title)
- Meta descriptions that are just the product description truncated (fix: write purposeful descriptions)
- Missing alt text on product images (fix: use
[Product Name] - [Color/View]format)
Step 4: Handle technical SEO issues
Canonical URLs for filter pages:
- Collection pages with active filters (
/collections/shoes?color=red) should use self-referencing canonicals - Pages with sort order only (
/collections/shoes?sort=price-asc) should canonical back to the unfiltered collection URL
In Shopify, this is handled automatically. In WooCommerce with Yoast, go to Yoast SEO → Search Appearance → Taxonomies and configure canonical behavior for filtered pages.
Robots.txt — block these paths:
/cartand/checkout— not indexable/accountand/search?— not indexable- Collection filter combinations with 3+ active filters — use
noindex, followmeta tag
In Shopify: Online Store → Themes → Edit Code → robots.txt.liquid In WooCommerce: Yoast SEO manages robots.txt automatically
Step 5: Verify with Google tools
1. Google Rich Results Test (search.google.com/test/rich-results): paste any product URL and verify structured data is correct 2. Google Search Console: check for structured data errors under Enhancements → Products 3. PageSpeed Insights: test Core Web Vitals — target LCP under 2.5 seconds, CLS under 0.1
Best Practices
- Write unique meta descriptions for every product — avoid duplicating the product title; include key attributes (size, material, price) that help click-through rate
- Compress and properly size product images — oversized images are the #1 cause of slow LCP scores; Shopify compresses automatically; WooCommerce use ShortPixel or Imagify plugin
- Use JSON-LD over microdata — easier to maintain and Google recommends it
- Set canonical URLs on every page — self-referencing canonicals prevent duplicate content from URL parameters
- Update sitemaps automatically — Shopify and WooCommerce + Yoast do this; for custom builds, regenerate on product publish/unpublish events
- Add image sitemaps — include product images with descriptive alt text for Google Image search traffic
Common Pitfalls
| Problem | Solution |
|---|---|
| Products not appearing in Google Shopping rich results | Check Google Search Console → Enhancements → Products for structured data errors; use Rich Results Test to validate |
| Faceted navigation creating millions of indexable URLs | Shopify/WooCommerce handle this automatically with canonicals; for custom builds, use noindex, follow on heavily filtered pages |
| Out-of-stock products returning 404 | Keep the page live at 200 status; show "out of stock" and suggest alternatives; remove from sitemap only if permanently discontinued |
| Schema.org validation errors | Test with Google's Rich Results Test; ensure price and availability are always present and correctly formatted |
| Slow Core Web Vitals hurting ranking | Preload hero images, compress product images, use lazy loading below fold; Shopify's CDN helps significantly |
Related Skills
- @google-shopping-feed
- @google-ads-ecommerce
- @content-commerce
- @social-proof-widgets
{
"context": "Tests whether the agent implements the correct canonical URL strategy for e-commerce pages (stripping variant params on product pages, keeping only allowed filter params on collection pages, sorting params alphabetically), generates correct robots.txt directives, and applies correct meta robots noindex rules.",
"type": "weighted_checklist",
"checklist": [
{
"name": "Product canonical strips variants",
"max_score": 9,
"description": "Canonical URL for a product page always points to the base product URL — query parameters including variant selectors are stripped"
},
{
"name": "Collection allowed filter params",
"max_score": 9,
"description": "Canonical URL for collection pages retains only these filter params: color, size, brand, material, price — other params (sort, page, tracking) are stripped"
},
{
"name": "Canonical params sorted alphabetically",
"max_score": 8,
"description": "The retained filter query parameters in the collection canonical URL are sorted alphabetically (e.g. brand before color before size)"
},
{
"name": "Paginated prev/next links",
"max_score": 9,
"description": "Paginated collection pages include rel='prev' and/or rel='next' link tags; the first page has no prev; the prev link for page 2 points to the base path without any page parameter"
},
{
"name": "robots.txt sort param disallow",
"max_score": 7,
"description": "robots.txt contains a Disallow rule for /collections/*?sort= to block sort-parameter URLs"
},
{
"name": "robots.txt page param disallow",
"max_score": 7,
"description": "robots.txt contains a Disallow rule for /collections/*?page= to block paginated collection URLs"
},
{
"name": "robots.txt transactional disallows",
"max_score": 7,
"description": "robots.txt disallows /cart, /checkout, /account, and /api/ paths"
},
{
"name": "robots.txt search disallow",
"max_score": 7,
"description": "robots.txt contains a Disallow rule for /search? to block search result pages"
},
{
"name": "Heavy filter noindex",
"max_score": 9,
"description": "Collection pages with more than 2 active filters receive a 'noindex, follow' meta robots value; pages with 2 or fewer active filters are indexable"
},
{
"name": "Search results noindex",
"max_score": 7,
"description": "Search result pages receive 'noindex, follow' meta robots"
},
{
"name": "Default pages indexable",
"max_score": 7,
"description": "Product pages and standard collection pages (with 0-2 filters) return 'index, follow' meta robots"
},
{
"name": "Self-referencing canonical on all pages",
"max_score": 7,
"description": "Every generated page context includes a canonical tag, including pages with no special filter/variant logic"
},
{
"name": "Sitemap reference in robots.txt",
"max_score": 7,
"description": "robots.txt includes a Sitemap directive pointing to the sitemap URL"
}
]
}
URL Canonicalization and Crawl Control Library
Problem Description
A mid-sized outdoor apparel retailer has recently hired an SEO consultant who discovered two serious issues with their current storefront: Google is indexing thousands of near-duplicate pages generated by their faceted navigation (combinations of color, size, brand filters combined with sort orders and pagination), and some of their product variant pages are competing against each other in search results. The consultant has recommended a systematic approach to URL canonicalization and crawler directives as the first step to fixing their duplicate content problem.
The engineering team needs to build a utility module that can be integrated into their Node.js storefront backend. The module should handle three distinct concerns: determining the correct canonical URL for any given page request, generating the appropriate robots crawl directives for different page types, and producing a robots.txt file. The module will be called at render time by the server to inject the correct meta tags into each page's <head>.
Output Specification
Write the solution as a TypeScript module seo-utils.ts that exports the necessary functions.
Also create a demo.ts file that calls these functions with a variety of representative inputs and logs the results. The demo should cover at minimum:
- A product page URL with a variant parameter in the query string
- A collection page URL with multiple filter parameters including sort and page
- A collection page URL with only 1 filter parameter
- A paginated collection (page 1, page 2, and a middle page)
- A heavily filtered collection page with multiple concurrent filter facets applied
- A search results page
- A standard product page
Also output the generated robots.txt content to a file called robots.txt.
No build toolchain required — plain TypeScript files are fine. All outputs should be written to the working directory so they can be reviewed.
{
"context": "Tests whether the agent correctly builds a product JSON-LD schema using Schema.org types, handles single vs. aggregate offers, conditionally includes aggregate ratings, and uses proper availability URLs. Also tests Open Graph meta tag completeness and correct price formatting.",
"type": "weighted_checklist",
"checklist": [
{
"name": "JSON-LD script tag",
"max_score": 8,
"description": "Structured data is output inside a <script type=\"application/ld+json\"> tag (not microdata attributes in HTML elements)"
},
{
"name": "Schema.org context",
"max_score": 6,
"description": "JSON-LD object includes '@context': 'https://schema.org' and '@type': 'Product'"
},
{
"name": "Required Product fields",
"max_score": 10,
"description": "JSON-LD Product object includes all of: name, image (as array), description, sku, mpn, and brand (as an object with @type 'Brand')"
},
{
"name": "Description truncation",
"max_score": 6,
"description": "Product description in JSON-LD uses a dedicated SEO/meta description field when available, otherwise truncates to 200 characters"
},
{
"name": "Single vs aggregate offers",
"max_score": 10,
"description": "Single-variant products use @type 'Offer'; multi-variant products use @type 'AggregateOffer' with lowPrice, highPrice, and offerCount"
},
{
"name": "Schema.org availability URLs",
"max_score": 8,
"description": "Offer availability uses full schema.org URL strings: 'https://schema.org/InStock' or 'https://schema.org/OutOfStock' (not short strings like 'instock')"
},
{
"name": "Conditional aggregateRating",
"max_score": 8,
"description": "aggregateRating is only added to the Product schema when review count is greater than 0; products with no reviews do not include aggregateRating"
},
{
"name": "AggregateRating fields",
"max_score": 6,
"description": "When present, aggregateRating includes ratingValue (formatted to 1 decimal place), reviewCount, bestRating: '5', worstRating: '1'"
},
{
"name": "Price as decimal string",
"max_score": 8,
"description": "Prices in offers are formatted as a 2-decimal-place string (e.g. '19.99') converted from integer cents, not as raw integers"
},
{
"name": "OG product type",
"max_score": 8,
"description": "Open Graph meta tags include og:type='product' and product:price:amount, product:price:currency, and product:availability"
},
{
"name": "OG availability values",
"max_score": 8,
"description": "product:availability meta tag uses the correct OG values: 'instock', 'oos', or 'preorder' (not schema.org URL strings)"
},
{
"name": "Twitter card type",
"max_score": 6,
"description": "Twitter meta includes twitter:card='summary_large_image'"
},
{
"name": "Breadcrumb JSON-LD",
"max_score": 8,
"description": "Page also includes a separate BreadcrumbList JSON-LD block with @type='BreadcrumbList' and itemListElement as a list of ListItem objects with position (1-indexed), name, and item (full URL)"
}
]
}
Product Page SEO Module
Problem Description
A fashion retailer is launching a new direct-to-consumer storefront. Their marketing team has been told by their SEO agency that product pages need to be "structured data ready" to qualify for Google Shopping rich results and achieve strong organic rankings before their Q3 launch. The engineering team has been asked to build a reusable TypeScript module that generates all SEO-relevant markup for a product page.
The module needs to support two product shapes: simple products (single option, one price) and configurable products (multiple variants with different prices). Reviews are stored separately and may or may not exist for every product. The agency flagged that a previous implementation had validation errors in Google Search Console because availability was set incorrectly and ratings appeared on products with no reviews.
Output Specification
Write the module as a single TypeScript file product-seo.ts. It should export the functions needed to produce the full SEO markup for a product page, including:
- Open Graph and standard HTML meta tags
- Machine-readable structured data markup for Google rich results, embeddable in an HTML page
<head>
Also create a short demo.ts (or equivalent runnable script) that exercises the module with representative test data covering:
- A single-variant product with reviews
- A multi-variant product without reviews
- An out-of-stock product
The demo should print or log the generated outputs so the results can be inspected. No build toolchain is required — plain .ts files with type annotations are fine if the logic is clear.
{
"context": "Tests whether the agent generates a correct XML sitemap index with gzip-compressed child sitemaps for products, collections, and pages, includes image entries in product sitemaps, uses the sitemap npm package, applies correct field values (changefreq, priority), and pings Google after generation.",
"type": "weighted_checklist",
"checklist": [
{
"name": "sitemap npm package used",
"max_score": 9,
"description": "The solution imports from the 'sitemap' package (e.g. SitemapStream, streamToPromise) rather than building XML string concatenation manually for product sitemaps"
},
{
"name": "Sitemap index structure",
"max_score": 9,
"description": "Generated sitemap index XML contains entries for at least: a products sitemap, a collections sitemap (collections.xml.gz), and a pages sitemap (pages.xml.gz)"
},
{
"name": "50,000 URL limit per child sitemap",
"max_score": 10,
"description": "Product data is split across multiple child sitemaps with a maximum of 50,000 URLs per file (i.e. Math.ceil(total / 50000) files are created)"
},
{
"name": "Gzip compression",
"max_score": 9,
"description": "Child sitemap files are gzip-compressed (e.g. using gzipSync from 'zlib') and saved with a .gz extension"
},
{
"name": "Product sitemap filename pattern",
"max_score": 7,
"description": "Product child sitemap files follow the naming pattern products-{N}.xml.gz (e.g. products-1.xml.gz, products-2.xml.gz)"
},
{
"name": "Product entry fields",
"max_score": 9,
"description": "Each product entry in the sitemap includes: url, lastmod (ISO 8601 string), changefreq set to 'daily', and priority set to 0.8"
},
{
"name": "Image entries in sitemap",
"max_score": 9,
"description": "Product sitemap entries include an img array with at least url and title per product image"
},
{
"name": "Image title fallback",
"max_score": 7,
"description": "Image title uses the image's alt text when available, falling back to the product title"
},
{
"name": "Google ping after generation",
"max_score": 8,
"description": "After sitemaps are generated, the script sends an HTTP request to https://www.google.com/ping?sitemap=... to notify Google"
},
{
"name": "Sitemap index uses lastmod",
"max_score": 7,
"description": "Each <sitemap> entry in the index includes a <lastmod> element with the current timestamp in ISO format"
},
{
"name": "Offset-based pagination",
"max_score": 7,
"description": "Product sitemaps use offset-based database queries (limit=50000, offset=(page-1)*50000) to paginate through the full catalog"
},
{
"name": "Sitemap output in public directory",
"max_score": 9,
"description": "Sitemap index is saved as sitemap.xml in the public directory root; child sitemaps go into a public/sitemaps/ subdirectory"
}
]
}
Automated Sitemap Generation for Large Product Catalog
Problem Description
A home goods retailer has grown their online catalog to over 120,000 active product SKUs across 400+ collections. Their current sitemap is a single hand-maintained XML file that hasn't been updated in months and is causing Google Search Console to report that many new products are not being indexed. The SEO team has flagged this as a top priority before their upcoming product line launch.
The engineering team needs to build an automated sitemap generation script that can handle a catalog of this size and be run on a schedule. The script should produce all necessary sitemap files and ensure that search engines are notified when they are regenerated. Product images are also important for the business — they drive significant traffic through Google Image Search — so those should be represented in the sitemap too.
The team is working in a Node.js/TypeScript environment. They've noted that the existing code in their codebase uses a consistent pattern of storing prices in integer cents, and their product records include an updatedAt timestamp and an images array where each image has src and alt properties.
Output Specification
Write the solution as a TypeScript script regenerate-sitemaps.ts that, when run, generates the complete sitemap file set.
Since there is no live database available, use the following mock data module as a stand-in. Extract the file below before starting, then import it in your script.
The script should write output sitemap files to disk so they can be inspected.
Also write a brief sitemap-plan.md document (1 page) explaining the structure of the sitemap output, what files are created, how products are distributed across child sitemaps, and how the script should be integrated into a production workflow (e.g. when to trigger regeneration, how to schedule it).
=============== FILE: mock-db.ts =============== // Mock database module for sitemap generation // In production this would query a real database
export interface Product { id: number; slug: string; title: string; updatedAt: Date; images: { src: string; alt: string }[]; }
export interface Collection { slug: string; title: string; updatedAt: Date; }
// Simulate a catalog of 120,000 products split across pages export const db = { products: { async countActive(): Promise<number> { return 120000; }, async findActive(opts: { limit: number; offset: number }): Promise<Product[]> { const products: Product[] = []; const start = opts.offset + 1; const end = Math.min(opts.offset + opts.limit, 120000); for (let i = start; i <= end; i++) { products.push({ id: i, slug: product-${i}, title: Home Goods Item ${i}, updatedAt: new Date('2026-03-10T08:00:00Z'), images: [ { src: https://cdn.example.com/products/${i}/main.jpg, alt: i % 3 === 0 ? Home Goods Item ${i} - Front View : '', }, ], }); } return products; }, }, collections: { async findPublished(): Promise<Collection[]> { return Array.from({ length: 400 }, (_, i) => ({ slug: collection-${i + 1}, title: Collection ${i + 1}, updatedAt: new Date('2026-03-11T08:00:00Z'), })); }, }, };
{
"name": "finsi/ecommerce-seo",
"version": "0.1.0",
"summary": "Product page SEO, structured data (JSON-LD), canonical URLs, and sitemap generation",
"skills": {
"ecommerce-seo": {
"path": "SKILL.md"
}
}
}