How to slugify or transform string values inside GROQ queries?
Yes, GROQ has string manipulation functions that can help you transform strings, though there isn't a built-in slugify() function. You can achieve some string transformations using the available string namespace functions.
Basic string transformations in GROQ
GROQ provides several string functions in the string:: namespace:
lower(string)andupper(string)- convert to lowercase/uppercasestring::split(string, separator)- splits a string into an arrayarray::join(array, separator)- joins array elements into a stringstring::startsWith(string, prefix)- checks if string starts with a prefix
Creating a slug-like transformation
While there's no native slugify() function, you can combine these functions for basic transformations:
*[_type == "navigation"] {
items[] {
// Convert to lowercase
"slug": coalesce(
page->slug.current,
lower(title)
),
children[] {
"slug": page->slug.current,
}
}
}You could also use string::split() and array::join() to replace spaces with hyphens:
*[_type == "navigation"] {
items[] {
"slug": coalesce(
page->slug.current,
array::join(string::split(lower(title), " "), "-")
),
children[] {
"slug": page->slug.current,
}
}
}However, GROQ has limitations for complex string manipulation:
- No
string::replace()function for character substitution (despite what you might find in searches) - Can't easily remove special characters, handle accents, or apply complex slugification rules
- String functions are relatively basic compared to JavaScript
Better approach: Transform after fetching
For proper slugification (handling special characters, accents, multiple spaces, etc.), I'd recommend doing the transformation in your application code after fetching the data:
import slugify from 'slugify' // or your preferred slugify library
const data = await client.fetch(`
*[_type == "navigation"] {
items[] {
"slug": coalesce(page->slug.current, title),
children[] {
"slug": page->slug.current,
}
}
}
`)
// Transform the slugs that need it
const processed = data.map(nav => ({
...nav,
items: nav.items.map(item => ({
...item,
slug: item.slug?.includes?.('/') ? item.slug : slugify(item.slug, {lower: true})
}))
}))This gives you much more control and handles edge cases like special characters, accents, and proper URL encoding that GROQ can't easily handle on its own. The string namespace functions are great for simple transformations, but complex string manipulation is better handled in your application layer.
Show original thread6 replies
Sanity – Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.