Sanity GROQ conventions
Conventions for keeping GROQ readable and consistent once a project grows past its first few queries. The recurring theme: queries are composed from named fragments, named predictably, and project only what the page needs.
File organisation
- Import
defineQuery(andgroqif needed) fromnext-sanityat the top of query files - Export queries as constants through
defineQueryso tooling and typegen can find them - Organise queries by content type (blogs, pages, products) and group related queries together
- Define common projection fragments at the top of the file, before the queries that use them
Naming conventions
- camelCase for all query names
- Prefix with an action verb followed by the content type:
getAllBlogPosts,getPageBySlug - Suffix every query with
Query:getAllBlogIndexTranslationsQuery - Prefix reusable fragments with an underscore:
_richText,_buttons,_icon
Fragment reuse
Create a named fragment for every repeated projection and interpolate it:
Keep fragments composable and focused on one concern each. A fragment that projects half the document defeats the purpose.
Images
When a query touches an image, do not expand it (image{...} with asset dereferencing) unless explicitly required. Pass the image reference to the frontend image builder instead; expanding assets in GROQ bloats every response that includes the fragment.
Parameters
- Use
$parameters for every dynamic value:$slug,$locale,$id. Never interpolate user input into the query string. - Handle localisation with a consistent pattern shared across queries (a
${localeMatch}fragment beats five subtly different filters). - Use
select()for conditional logic inside queries andcoalesce()for defaults. - Add pagination parameters (
[$start...$end]) to every list query before the list gets long, never after.
Response types
- Export TypeScript types for query responses when typegen does not cover the case
- Make the type names match the query:
export type GetAllMainPageTranslationsQueryResponse = string[];
Query practices
- Filter explicitly on type:
_type == "blog"beats implicit shape matching - Project the fields the page needs; returning whole documents couples every consumer to the full schema
- Sort with
order()explicitly instead of relying on document order - Check
defined(field)before filtering or sorting on optional fields - Use conditional projections for fields that only exist on some variants
Code style
- Template literals for query strings, with nested structures indented for readability
- Keep related query parts together and maintain consistent whitespace
- Comment the intent of complex filters; the GROQ itself rarely explains why
About this skill
Maintained by Roboto Studio, a UK agency that has written GROQ for production Sanity projects since the early days of the platform. It distills the query conventions we hold every project to. If you would rather have it done for you: robotostudio.com/services/sanity.
Licensed MIT. Wow, I can't believe people are actually using these. Tell me if it worked: yo@robotostudio.com