Diagnosing and Fixing Slow GROQ Queries in Sanity Studio
Identify why a GROQ query takes >2 seconds, apply targeted checks, and apply fixes that reduce latency while preserving UI functionality.
01 Feb 2026, 02:26 UTC

Recognizable condition
When you open a document list or custom view in Sanity Studio, the Vision plugin shows a spinning indicator and the Network tab reports a request to /data/query that takes longer than 2 seconds or times out. The UI may appear unresponsive or show incomplete data.
Short cause/diagnostic table
| Symptom | Likely cause |
|---|---|
| Query returns slowly (>2 s) or times out | Missing or overly broad filters, projection of * or large block fields, no limit, deep reference traversal, or querying a very large dataset without pagination |
| High latency but small payload | Expensive joins via references or unindexed filter fields |
| Large payload (>1 MB) with slow response | Projection of unnecessary fields (e.g., full block content) or returning too many documents |
Ordered checks
- Inspect the current GROQ query
Open the Vision plugin, locate the query editor, and copy the exact GROQ string.
// Example of a slow query *[_type == "article"] { title, body, author->{ name, picture } }Look for:
- Use of
*without a type filter - Projection of large fields like
body(block content) or entire arrays - Missing
limitor pagination syntax - Reference traversals that go deeper than one level (
->chained)
- Use of
- Add a sensible type filter
If the query does not already constrain
_type, add it. This reduces the document set the engine must scan.*[_type == "article" && ...] { ... } - Apply a date or other scoped filter
For time‑based lists, restrict to a relevant window (e.g., last 6 months). Replace
$datewith a parameter or literal.*[_type == "article" && publishedAt > $date] { ... } - Replace
*projection with needed fields onlyProject only the fields the UI actually displays. This cuts payload size and avoids loading large block content.
*[_type == "article" && publishedAt > $date] { title, slug.current, _updatedAt, author->{ name, slug.current } } - Add a limit or pagination
If the view shows a list, cap the result set to the page size (e.g., 20‑50). Use
[0..$limit]syntax for offset‑based pagination.*[_type == "article" && publishedAt > $date] { title, slug.current }[0..50] - Check reference depth
If you need data from a referenced document, fetch only the sub‑fields you require. Avoid chaining multiple
->unless absolutely needed.*[_type == "article" && publishedAt > $date] { title, author->{ name, picture } }[0..30]
Fixes tied to findings
- Unfiltered
*→ Add_type == 'yourType' and any relevant property filters. - Projection of large fields → Replace
*orbodywith explicit field list; if block content is needed, consider usingportableTextwith[]to get only plain text. - Missing limit → Append
[0..N]where N matches the UI page size. - Deep reference traversal → Flatten the projection to a single
->level or denormalize frequently needed data into the parent document. - Very large dataset (>100k docs) → After applying the above, if latency remains >2 s, consider enabling the CDN cache (
apiVersionwithcdn: true) or contact Sanity support with the query profile.
Verification
- Run the original query in Vision and note the response time shown in the Network tab (look for
Duration). - Apply one change at a time (e.g., add type filter, then limit, then projection). After each edit, re‑run the query and record the new duration.
- Confirm that the payload size (visible under
Response→Size) has decreased proportionally to the fields removed. - Check the Studio UI that consumes the query (e.g., a document list) to ensure it still displays the expected titles, slugs, and any required data.
- If the UI shows missing data, adjust the projection to include the omitted field and re‑test.
Escalation criteria
- Latency remains >2 seconds after implementing type filter, sensible date filter, field projection, and a limit of 50‑100 documents.
- Dataset size exceeds 100 k documents and the query still scans a large fraction of it despite filters.
- Reference traversals are required for more than one level and denormalization is not feasible.
When any of the above applies, collect the query string, the execution plan (Vision shows a Query profile button), and the Network timing details, then open a support ticket with Sanity, attaching this information.
Limitations and practical checks
Applying a low limit may hide relevant results; ensure the limit matches the UI’s pagination mechanism (e.g., infinite scroll or page numbers). Removing the * projection without verifying field usage can break components that rely on hidden data; always test in a preview or branch environment before promoting to production.
To verify that the fix does not break dependent components, open the relevant Studio view in a preview environment, interact with the list (click items, open dialogs), and confirm that all expected fields are present. If a field is missing, add it back to the projection and re‑measure latency.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.