---
name: sanity-groq-conventions
description: Write maintainable GROQ for Sanity projects. Covers query file organisation, naming conventions, reusable projection fragments, parameters and localisation patterns, response typing, and query performance practices like explicit filtering and projection over whole documents. Use when writing or reviewing GROQ queries, structuring a queries file, or wiring Sanity data fetching into a frontend.
license: MIT
metadata:
  author: roboto-studio
  version: "1.0.0"
  updated: "2026-06-11"
  homepage: https://robotostudio.com/skills/sanity-groq-conventions
---

# 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` (and `groq` if needed) from `next-sanity` at the top of query files
- Export queries as constants through `defineQuery` so 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:

```typescript
const _buttons = /* groq */ `
  buttons[]{
    _key,
    label,
    variant,
    "href": url.href
  }
`;

export const getPageBySlugQuery = defineQuery(`
  *[_type == "page" && slug.current == $slug][0]{
    title,
    ${_buttons}
  }
`);
```

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 and `coalesce()` 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](https://robotostudio.com), 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](https://robotostudio.com/services/sanity).

Licensed MIT. Wow, I can't believe people are actually using these. Tell me if it worked: yo@robotostudio.com
