Skip to main content

Overview

Spree generates SEO-friendly URL slugs for resources like products, taxons, and stores. Instead of accessing resources by ID, you can use clean, readable URLs based on resource names. Both the Store API and Admin API accept slugs or IDs interchangeably for resource lookups.
// Both work — slug or ID
const product = await client.products.get('spree-tote')
const product = await client.products.get('prod_86Rf07xd4z')

// Taxons use permalink slugs
const taxon = await client.taxons.get('categories/clothing')

Slug Generation

Slugs are automatically generated from resource names:
InputGenerated Slug
Spree T-Shirtspree-t-shirt
Café & Restaurantcafe-and-restaurant
Summer Collection 2025summer-collection-2025
If a slug already exists, Spree appends the SKU or a unique identifier to ensure uniqueness.

Models with Slugs

ResourceSlug ColumnTranslatableHierarchical
ProductslugYesNo
TaxonpermalinkYesYes
StorecodeNoNo
PostslugYesNo
Taxon slugs include the full parent path, making them hierarchical:
categories                     → "categories"
categories/clothing            → "categories/clothing"
categories/clothing/t-shirts   → "categories/clothing/t-shirts"
When a parent taxon is renamed, all child permalinks update automatically.

Slug History

When a slug changes (e.g., a product is renamed), Spree preserves the old slug. Requests using old slugs still find the resource, enabling:
  • SEO continuity — no broken links when names change
  • Bookmark compatibility — saved URLs keep working
  • Graceful URL migration — old links resolve automatically

Internationalization

Products, taxons, and posts support localized slugs — a different slug per locale:
// English
const product = await client.products.get('red-shoes')

// French (with locale header)
const product = await client.products.get('chaussures-rouges', {
  locale: 'fr',
})
Slugs are unique within the same locale but can be duplicated across different locales.

Reserved Words

Spree prevents certain words from being used as slugs to avoid route conflicts: new, edit, index, login, logout, admin, and others.