How I built a 400-page glossary on Shopify with programmatic SEO
Most programmatic SEO setups run on Next.js, headless CMSs, or database-driven stacks. Shopify isn't the obvious choice. But sometimes you're working with what's already there: an existing Shopify store with traffic, a checkout flow, and a brand. Migrating to a different stack just for SEO pages doesn't make sense.
So I built a full glossary system directly on Shopify. 100+ terms across 4 languages. 400+ pages total. All managed through Markdown files and a single import script.
Here's how it works and what I learned.
Why a glossary
Glossaries are one of the best pSEO plays for niche industries. Each term is a long-tail keyword page. They interlink naturally. They build topical authority. And they're the kind of content Google actually wants to show for informational queries.
The use case here was a B2B company in a specialized industry. Technical terminology that people actually search for: methods, processes, product comparisons. Not keyword-stuffed filler.
The goal was to own the informational layer around the product category in four languages.
The stack
Shopify doesn't have a "programmatic pages" feature. But it has metaobjects, which are basically custom data types you define yourself. Think of them like a mini-CMS inside Shopify.
I created a glossary_term metaobject with fields like:
term- the display nameslug- URL identifierlanguage- language code (e.g. en, fr, de)category- for grouping related termsshort_definition- a 1-2 sentence snippet (optimized for featured snippets)full_content- the main body (500-800 words)faq_json- FAQ pairs, stored as JSONseo_title/seo_description- custom meta tags- Cross-language slug fields (
en_slug,fr_slug, etc.) - for hreflang linking
Each metaobject entry becomes a page. You assign a Liquid template to render it. That's the whole trick.
Content lives in Markdown
I didn't write content inside Shopify. That would be painful for 340 entries.
Instead, every glossary term is a Markdown file with frontmatter:
---
term: "Supply Chain Visibility"
slug: "supply-chain-visibility"
category: "Logistics"
target_keyword: "supply chain visibility software"
languages:
fr_slug: "visibilite-chaine-approvisionnement"
fi_slug: "toimitusketjun-nakyvyys"
de_slug: "lieferketten-transparenz"
---
# Supply Chain Visibility: What It Means and How It Works
[Body content here...]
## Frequently Asked Questions
**What is supply chain visibility?**
Supply chain visibility refers to...
The FAQ section gets automatically parsed into structured JSON. The first paragraph becomes the featured snippet text. Everything else flows into the main body.
Organized in folders by language: /glossary/en/, /glossary/fr/, etc.
One script to push everything
A Node.js script reads the Markdown files, converts them to Shopify's rich text JSON format, and upserts them via the Admin API.
node import.js --lang=en # all English terms
node import.js --lang=fr tDCS neuromodulation # specific French terms
node import.js --delete en-old-term # remove one
The script handles:
- OAuth authentication - uses Shopify's Client Credentials grant instead of static API tokens
- Markdown to rich text conversion - Shopify doesn't accept Markdown, so headings, lists, bold, and links all get converted to their JSON rich text format
- Automatic cross-linking - this is the best part
Auto cross-linking
This was the feature that made the whole system worth building.
When importing a term, the script scans the body text for mentions of other glossary terms in the same language. When it finds one, it wraps it in a link to that term's page.
So if the "Last Mile Delivery" page mentions "supply chain visibility," that word automatically becomes a link to /pages/glossary-term/supply-chain-visibility.
To avoid false matches, it sorts terms by length (longest first) and uses word-boundary matching. It also skips self-references so a page doesn't link to itself.
The result is a glossary where every page links to the others naturally. No manual linking needed.
The Liquid template
Two templates power the whole thing:
Term page renders each glossary entry with:
- Schema markup (DefinedTerm + FAQPage + BreadcrumbList)
- Hreflang tags connecting all four language versions
- A styled definition box at the top (featured snippet bait)
- Collapsible FAQ section using
<details>elements - Dynamic related terms pulled from the same category
- CTA section linking to products and pillar content
Index page is a browsable directory with:
- Live search filter
- A-Z alphabetical navigation
- Category labels
- Language switcher
- Term count
Both templates get deployed to the Shopify theme via the REST Assets API. Another script handles that: node push-all-templates.js.
Multilingual routing
Shopify's built-in translation system handles locale prefixes (e.g. /fr/, /de/). But glossary metaobjects are separate entries per language, not translations of one entry.
So the template includes a small JavaScript snippet that detects when the user's store locale doesn't match the entry's language. If someone on the French store lands on an English glossary term, it checks for a French equivalent and redirects.
The cross-language slug fields make this possible. Each entry knows its counterparts in other languages.
Structured data that actually matters
Every glossary page outputs three types of schema markup:
- DefinedTerm - tells Google this is a definition page
- FAQPage - for entries with FAQ sections (most of them), which enables FAQ rich results
- BreadcrumbList - clean navigation trail
The index page uses DefinedTermSet to connect all terms as a collection.
This isn't optional decoration. For informational queries in specialized fields, structured data is what gets you rich results in search. The short definition field is written specifically for featured snippets.
What makes this manageable
The whole system is designed so that adding a new term means creating one Markdown file and running one command.
No Shopify admin clicking. No copy-pasting into rich text editors. No manually creating links between pages.
The workflow:
- Write a Markdown file with frontmatter
- Run
node import.js --lang=en new-term - It's live
For teams that aren't technical, they can use something like Cursor to edit the Markdown files. It's just text with some structure at the top.
Why Shopify and not a headless setup
The obvious question. A headless CMS or a Next.js build would give more control. But:
- The store already exists and has traffic
- Checkout, cart, and product pages are already on Shopify
- Moving SEO pages to a subdomain or different stack splits your domain authority
- Shopify handles hosting, CDN, SSL, and uptime
- Metaobjects are surprisingly capable once you stop fighting them
The tradeoff is that Liquid templates are less flexible than React components, and Shopify's rich text format is annoying to work with. But the import script hides all of that.
Lessons learned
Metaobjects are underused. Most Shopify stores treat them as product metadata. They're actually a decent lightweight CMS for non-product content.
Markdown as source of truth works. It's version-controllable, diff-able, and AI tools can generate it. The import script is the bridge between "nice to write" and "what Shopify needs."
Cross-linking compounds. 100+ terms linking to each other is much stronger than 85 isolated pages. Google sees the depth.
Four languages from day one was the right call. Each language multiplies your search volume. And since the content structure is identical, adding a new language is just translation + one import command.
Featured snippet optimization pays off fast. Short, clear definitions at the top of each page. That's what Google pulls into position zero. The short_definition field exists for exactly this.
The numbers
100+ terms × 4 languages = 400+ glossary pages. Plus pillar pages and comparison content on top.
One import script. Two Liquid templates. Zero manual Shopify admin work after initial setup.
If you're on Shopify and think pSEO requires migrating to a developer-friendly stack, it doesn't. Metaobjects + Liquid + a good import script gets you surprisingly far.