Using GROQ queries in Structure Builder for custom ordering
Looking at your question, you're trying to use GROQ's order() clause within Structure Builder's documentList() to sort child documents based on custom calculations (like math operations on nested color picker values). The key issue is that Structure Builder's filtering system has limitations.
The short answer: No, you cannot use GROQ's order() clause directly in Structure Builder filters. However, there are two viable approaches to achieve custom ordering:
Option 1: Use defaultOrdering() with a pre-computed field (Recommended)
The Structure Builder's defaultOrdering() method requires a field name string, not a GROQ expression. The solution is to compute your ordering value and store it as a field in your document:
// In your schema
{
name: 'computedOrderValue',
type: 'number',
hidden: true, // Hide from editors if it's just for ordering
// Calculate this value when the color picker changes
}Then use it in Structure Builder:
S.documentList()
.title('Ordered Projects')
.filter('_type == "myType"')
.defaultOrdering([
{field: 'computedOrderValue', direction: 'desc'}
])You can compute this field using:
- A custom input component that calculates and sets the value when the color picker changes
- Document actions that compute it on save
- Sanity Functions with event handlers that react to document changes and update the field automatically
Option 2: Custom component view with GROQ
If pre-computing isn't feasible, create a custom component that fetches and displays documents with full GROQ ordering:
S.listItem()
.title('Custom Ordered List')
.child(
S.component(({documentStore}) => {
const [docs, setDocs] = useState([])
useEffect(() => {
const query = `*[_type == "myType"] | order(
color.hsv.v * color.hsv.s desc
)`
documentStore.getClient().fetch(query)
.then(setDocs)
}, [])
return (
<div>
{docs.map(doc => (
<DocumentListItem key={doc._id} doc={doc} />
))}
</div>
)
})
)Why the limitation exists
As noted in the Structure Builder reference, the filter() method "does not support joins, since they operate on individual documents, and will ignore order-clauses and projections." This is because Structure Builder uses realtime listeners that need to efficiently determine which documents match the filter as changes occur.
The defaultOrdering() method works differently—it tells the UI how to sort the already-filtered results, but it can only reference actual document fields, not computed expressions.
About the terminology confusion
You mentioned getting "faceblind" with terms like list, listItem, listType, etc. Here's a quick breakdown:
list()- Creates a pane containing multiple itemslistItem()- A single clickable item within a listdocumentList()- A specialized list that shows documents matching a filterdocumentListItem()- A single item representing a document
The Structure Builder reference has the full breakdown of each method.
Bottom line: Pre-compute your ordering value into a document field, then reference it in defaultOrdering(). This gives you the full power of custom calculations while working within Structure Builder's constraints.
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.