Sanity Studio Bloat: Audit & Prune Unused Document Types
By Nayan Kyada · · 9 min read
Part of The Sanity + Next.js Guide
Why Sanity Studio Performance Degrades Over Time
After two years on a client project, their Sanity Studio was loading 840 kB of JavaScript just to render the document list. The culprit: 47 schema types, of which only 22 were actively used in production. Every unused schema pulls in validation logic, preview components, and input field code — all of it bundled even if no editor ever touches that type.
With Sanity v5 placing greater emphasis on Studio build efficiency and modular schema loading, the cost of schema bloat is more visible than ever. I needed a safe audit path that wouldn't break existing content or CI. This post walks through the four-step process I use to identify, verify, and remove dead schemas without touching production documents.
Key takeaway: Unused schema types are not free. Each one contributes to your Studio JavaScript bundle, increases cold-load time for editors, and adds noise to your schema folder. Regular pruning is a maintenance habit worth building.
Step 1: List All Schema Types and Count Their Documents
The first step is understanding which document types actually exist in your dataset versus which ones are only defined in code.
Run a GROQ Count Query
I start with a GROQ query against the dataset to count documents per type. This runs in the Vision plugin or via the Sanity CLI:
// Run in Sanity Vision or via `sanity documents query`
{
"counts": *[!(_id in path("drafts.**"))] | order(_type asc)
| {"type": _type, "count": count(*[_type == ^._type])}
| group(_type) {"_type": _type[0], "total": sum(count)}
}This returns a flat list like {"_type": "pressRelease", "total": 0}. Any type with total: 0 is a candidate for removal. Export this to a CSV and cross-reference with the schema folder.
Interpret the Results
On that 47-schema project, the breakdown looked like this:
- 18 types had zero documents
- 7 types had fewer than 5 documents — all drafts created in 2023–2024 that were never published
- 22 types were actively used in production
That's 25 schemas consuming bundle space for no reason. Before deleting anything, though, you need to check for hidden dependencies.
Step 2: Check for Hidden References in Arrays and Blocks
A zero-document type might still be referenced inside portable text blocks or reference arrays. Deleting the schema without checking first can silently break previews, custom inputs, or desk structure resolvers.
Query for Cross-Document References
Run a second GROQ query to scan _ref fields:
// Find any document that references a given type ID
*[references(*[_type == "pressRelease"]._id)] {_id, _type}- If this returns an empty array, the type is safe to remove.
- If it returns results, check whether those parent documents are themselves orphaned.
On one project, teamMember had zero standalone docs but was referenced in an aboutPage singleton that was actively used by editors every week. I kept that schema.
Automate the Reference Check with a Node Script
Manually running GROQ for each candidate type is tedious. I script this check for all zero-count types using the Sanity CLI and Node:
// scripts/audit-refs.ts
import {createClient} from '@sanity/client'
const client = createClient({
projectId: 'abc123',
dataset: 'production',
useCdn: false,
apiVersion: '2024-01-01',
token: process.env.SANITY_TOKEN,
})
const candidateTypes = ['pressRelease', 'oldBlogCategory', 'legacyAuthor']
for (const type of candidateTypes) {
const refs = await client.fetch(
`*[references(*[_type == $type]._id)] {_id, _type}`,
{type}
)
console.log(`${type}: ${refs.length} references`)
}This takes about 90 seconds on a 12k-document dataset. Log results to a JSON file and review in VS Code before taking any action.
Step 3: Remove Schema Files and Measure the Bundle Delta
Once you've confirmed a type is unused and has no hidden references, you can safely remove it from the codebase.
The Removal Process
- Delete the schema file from schemas/ (e.g., schemas/pressRelease.ts)
- Remove the import from sanity.config.ts
- Run
sanity devlocally and confirm the Studio compiles without errors - Verify no TypeScript errors surface in related config files
Measure Bundle Size Before and After
NODE_ENV=production sanity build --statsThe --stats flag outputs a JSON file with chunk sizes. Compare before/after using a script that diffs sanity-build-stats.json. On the 47-schema project, removing 18 types reduced the main bundle from 840 kB to 680 kB — a 19% drop.
Manual Studio Smoke Test
After each schema removal:
- Open the Studio in a local browser
- Click through all active document types to verify previews load correctly
- Check that custom input components render as expected
- Confirm desk structure resolves without errors
Warning: Shared input components can be deceptive. On one project, a shared
linkFieldinput was imported by a deleted schema but also used by 12 active schemas. Deleting the schema didn't break the Studio — but I had to keep the shared input file and its dependencies intact.
Step 4: Deprecate Instead of Delete When Documents Might Return
Not every zero-count schema is safe to hard-delete. If stakeholders might revive a document type in a future sprint, deletion creates unnecessary migration work. Use schema deprecation instead.
Add hidden: true to the Schema Definition
// schemas/pressRelease.ts
import {defineType} from 'sanity'
export default defineType({
name: 'pressRelease',
type: 'document',
title: 'Press Release',
hidden: true, // Removes from Studio UI but keeps validation logic
fields: [
{name: 'title', type: 'string'},
// …
],
})What hidden: true does:
- Removes the type from the Studio's "Create new document" menu
- Hides it from desk structure navigation
- Old documents remain fully queryable via GROQ
- Existing content is preserved and unaffected
What hidden: true does NOT do:
- It does not remove the schema from the JavaScript bundle
- It does not free up any bundle size
- It does not prevent the type from appearing in API responses
Use this option for client-managed types that might get re-enabled in a future quarter, or for document types that exist only in a staging dataset for QA purposes.
Measuring Studio Bundle Impact Over Time
Treating Studio performance as a metric — not just a feeling — makes pruning decisions defensible and repeatable.
Track Metrics in a Committed JSON File
I track Studio bundle size in a studio-metrics.json file committed to the repo. After each schema prune, I log:
{
"date": "2026-05-04",
"totalSchemas": 29,
"mainBundleKB": 680,
"studioLoadTimeMs": 1240
}How to Measure Studio Load Time
- Open the Chrome DevTools Network panel
- Hard-refresh the Studio URL
- Record the "Load" event time at the bottom of the panel
On the 47-schema project, pruning 18 types dropped load time:
| Connection | Before | After | Improvement |
|---|---|---|---|
| Desktop Ethernet | 2.1s | 1.4s | −33% |
| Mobile 4G | 4.8s | 3.2s | −33% |
Use Lighthouse for JavaScript Coverage
Run Lighthouse on your Studio URL (https://yourproject.sanity.studio/) and check the JavaScript coverage report in DevTools. Unused schemas often pull in 60–80 kB of unreachable code per type — code that ships to every editor on every Studio load.
When NOT to Remove a Schema
Some schemas look unused but serve a critical purpose outside the Studio's visible document types. I never remove a schema if any of the following apply:
- It is referenced in a migration script that might be re-run against a dataset
- It is used in a webhook handler or custom API route outside of Sanity Studio
- It is part of a modular shared field (like a
seoobject orblockContentdefinition) used across multiple types - Documents were soft-deleted (moved to a separate dataset) but might be restored later
- The type exists only in a staging or preview dataset used for QA
Real-world example: On one project,
legacyBlogPosthad zero documents in production but was still used in a staging dataset for ongoing QA testing. Deleting the schema would have broken the staging Studio for the QA team — even though production was unaffected.
Building a Repeatable Audit Workflow
A one-time pruning effort is valuable. A repeatable workflow compounds that value over time.
Recommended Audit Schedule
- Every 6 months: Run the full four-step audit on all datasets
- When onboarding a new developer: Use the schema folder as a documentation checkpoint — if a schema can't be explained, investigate it
- After major feature launches: New features often deprecate old schema types; audit within 30 days of launch
Estimated Time Investment
- Initial audit on a 47-schema project: ~90 minutes
- Follow-up audit (6 months later, after establishing baseline): ~30 minutes
- Time saved per editor per week from faster Studio boot: 2–5 minutes (compounding across a team)
Checklist for Each Schema Candidate
- GROQ count query confirms zero published documents
- Reference scan confirms no parent documents depend on this type
- No migration scripts reference this type's
_typestring - No webhook or API handler filters on this type
- Shared input components are not exclusively imported by this schema
- Stakeholders confirmed the type will not be revived
Results Across Real Client Projects
Across four client projects audited in 2025–2026, schema pruning delivered consistent, measurable improvements:
| Project | Schemas Before | Schemas Removed | Bundle Reduction | Load Time Improvement |
|---|---|---|---|---|
| Client A (3-year-old project) | 62 | 31 | −320 kB | −1.2s |
| Client B (47 schemas) | 47 | 18 | −160 kB | −0.7s |
| Client C (30 schemas) | 30 | 9 | −90 kB | −0.5s |
| Client D (24 schemas) | 24 | 6 | −55 kB | −0.3s |
Average across all four projects:
- 16% reduction in Studio bundle size
- 0.8–1.2 second improvement in perceived load time
The largest single win was a 320 kB drop on a three-year-old project that had accumulated 62 schemas, 31 of which were completely unused.
Summary
Auditing and pruning unused Sanity document types is one of the highest-leverage Studio maintenance tasks available — with minimal risk when done methodically. The four-step process (count documents via GROQ, scan for hidden references, remove schema files and measure bundle delta, deprecate instead of delete when needed) consistently delivers 15–20% bundle reductions and measurably faster editor load times. With Sanity v5 making bundle efficiency a first-class concern, building a six-month audit cadence into your project workflow is a low-effort, high-return habit that improves the daily experience for every editor on your team.
Frequently asked questions
01How do I find unused document types in Sanity?
Run a GROQ count query grouped by `_type` against your dataset (Step 1 below) to find types with zero or near-zero documents. That's your candidate list — but always cross-check for hidden references (Step 2) before deleting anything, since a zero-document type can still be referenced inside portable text or another document's fields.
02Does removing a Sanity schema break existing content?
Deleting the schema file removes it from Studio's bundle and UI, but the underlying documents in your dataset are untouched and remain queryable via GROQ or the API — Sanity doesn't enforce schema at the database level. The risk is Studio-side: broken previews or desk-structure errors if something still imports or references that type. That's why Step 2 (checking for hidden references) matters more than the deletion itself.
03What does Sanity v5 change about Studio bundle size?
Sanity v5 places more emphasis on Studio build efficiency and modular schema loading, which makes existing schema bloat more visible in build stats and load-time metrics than in earlier versions. It doesn't automatically prune anything for you — the audit workflow in this post is still a manual, deliberate process.