Developer Docs

Content Schema Reference

Everything you need to add products, articles, and categories to Zubd.io. All content lives in src/content/ as MDX files.

Overview

Zubd.io is a static Astro site. Content is stored as MDX files (Markdown + JSX) inside src/content/products/ and src/content/articles/. Each content type has a Zod schema that validates frontmatter at build time — if a required field is missing or wrong, the build fails with a clear error.

Every piece of content exists in two files: one in en/ and one in ar/. Both must have the same handle value (which must also match the filename).

Golden Rule: filename = handle field = identical across EN and AR. Example: cerave-moisturizing-cream.mdx in both en/ and ar/, both with handle: "cerave-moisturizing-cream".

Product Schema

File path: src/content/products/en/{handle}.mdx

FieldTypeRequiredDescription
titlestringProduct display name
handlestringURL slug — must match filename, identical across locales
locale"en" | "ar"Language of this file
brandstringBrand name (e.g., "CeraVe")
modelstringProduct model/variant
categorystringCategory slug — auto-creates the category page
subcategorystringOptional sub-category
tagsstring[]Search/filter tags
imagestring (URL)Product image — use Unsplash URL or /images/products/
imageAltstringAccessible image description
gallerystring[]Additional image URLs (for gallery thumbs)
rating0–5 floatAggregate rating (e.g., 4.7)
reviewCountintegerNumber of reviews contributing to rating
affiliateUrlURL stringFull affiliate link — gets rel="nofollow sponsored"
affiliateLabelstringBuy button text (default: "Buy Now")
pricestringDisplay price string (e.g., "$24.99")
priceUpdatedISO date stringWhen price was last verified
metaTitlestringSEO title override (falls back to title)
metaDescriptionstring ≤320 charsSEO meta description
prosstring[]List of pros
consstring[]List of cons
userReviewsReview[]See review object below
publishedAtISO date stringPublication date (e.g., "2025-04-07")
updatedAtISO date stringLast updated date
draftbooleantrue = hidden from build (default: false)

User Review Object

FieldTypeDescription
authorstringReviewer name
rating0–5 floatReviewer's star rating
dateISO date stringReview date
textstringReview body text
source"amazon" | "google" | "manual"Where the review came from

Full Product Template

---
title: "Product Name"
handle: "product-name"
locale: "en"
brand: "Brand Name"
model: "Model Variant"
category: "category-slug"
tags: ["tag1", "tag2"]
image: "https://images.unsplash.com/photo-XXXXX?w=480&auto=format&fit=crop&q=80"
imageAlt: "Descriptive alt text"
rating: 4.5
reviewCount: 120
affiliateUrl: "https://amzn.to/XXXXXX"
affiliateLabel: "Buy on Amazon"
price: "$49.99"
priceUpdated: "2025-04-07"
metaDescription: "Short description under 160 chars for SEO."
pros:
  - "First pro"
  - "Second pro"
cons:
  - "First con"
userReviews:
  - author: "Jane D."
    rating: 5
    date: "2025-03-01"
    text: "Great product!"
    source: "amazon"
publishedAt: "2025-04-07"
draft: false
---

## Overview

Your review body in Markdown here...

Article Schema

File path: src/content/articles/en/{handle}.mdx

FieldTypeRequiredDescription
titlestringArticle headline
handlestringURL slug — must match filename
locale"en" | "ar"Language of this file
categorystringCategory slug
subcategorystringOptional sub-category
tagsstring[]Tags for discoverability
heroImagestring (URL)Hero image URL
heroImageAltstringHero image alt text
excerptstring ≤300 charsArticle summary shown in cards
featuredProductsstring[]Ordered list of product handles (rank #1 first)
metaTitlestringSEO title override
metaDescriptionstring ≤320 charsSEO meta description
authorstringAuthor name (default: "Zubd Editorial")
publishedAtISO date stringPublication date
updatedAtISO date stringLast major update
draftbooleantrue = hidden (default: false)
Tip: featuredProducts is the key field that connects an article to its product cards. List the product handle values in rank order — #1 first. The RankedProductList component automatically fetches and renders them.

Full Article Template

---
title: "Top 10 [Products] for [Use Case] (2025)"
handle: "top-10-products-use-case"
locale: "en"
category: "category-slug"
tags: ["tag1", "tag2"]
heroImage: "https://images.unsplash.com/photo-XXXXX?w=1200&auto=format&fit=crop&q=80"
heroImageAlt: "Descriptive hero image alt text"
excerpt: "Concise summary of what the article covers (max 300 chars)."
featuredProducts:
  - "product-handle-1"
  - "product-handle-2"
  - "product-handle-3"
metaTitle: "10 Best [Products] for [Use Case] (2025)"
metaDescription: "SEO description under 160 chars."
author: "Zubd Editorial"
publishedAt: "2025-04-07"
draft: false
---

## Why Trust This List?

Methodology paragraph...

## What to Look For

Key buying criteria...

Categories

Categories are automatically derived from the category field in your article frontmatter. There is no separate category file to create. When you publish an article with category: "fitness", the page /en/categories/fitness is automatically built and shows all articles in that category.

To add a new category, simply use a new category value in your article frontmatter. To add a human-readable label for it, add an entry to src/lib/utils.ts in the getCategoryLabel() function:

// src/lib/utils.ts → getCategoryLabel()
fitness: { en: 'Fitness', ar: 'اللياقة البدنية' },
food:    { en: 'Food & Kitchen', ar: 'الطعام والمطبخ' },
// Add your new category here:

Images (Unsplash)

All product and article images use Unsplash URLs directly — no image hosting required. Use this URL format:

https://images.unsplash.com/photo-{PHOTO_ID}?w=1200&auto=format&fit=crop&q=80

Common sizes:

  • Article hero: ?w=1200&h=630&fit=crop
  • Product image: ?w=480&h=480&fit=crop
  • OG image: ?w=1200&h=630&fit=crop

Find images at unsplash.com — copy the photo ID from the URL.

Publishing Workflow

  1. Create src/content/products/en/your-product.mdx
  2. Create src/content/products/ar/your-product.mdx (same filename)
  3. Add the product handle to an article's featuredProducts list
  4. Create src/content/articles/en/your-article.mdx
  5. Create src/content/articles/ar/your-article.mdx
  6. Run npm run build — Zod validates all frontmatter
  7. Push to main → Cloudflare Pages auto-deploys
Build Validation: If any frontmatter field is invalid, the build stops with a clear error message. This prevents broken content from reaching production. Use draft: true to save work-in-progress without publishing.

URL Structure

PageEN URLAR URL
Homepage/en/ar
Article/en/articles/{handle}/ar/articles/{handle}
Product/en/products/{handle}/ar/products/{handle}
Category/en/categories/{category}/ar/categories/{category}
Privacy/en/privacy/ar/privacy
Contact/en/contact/ar/contact
Docs/en/docs
Sitemap/sitemap-index.xml
llms.txt/llms.txt