Why Sanity Studio Performance Degrades Over Time
Studios that have been in production for a few years tend to accumulate schema types nobody uses any more — it's common to find a project where well under half of the registered types still hold live content. Every unused schema can pull in validation logic, preview components, and custom input code — all of it bundled even if no editor ever touches that type.
You need a safe audit path that won't break existing content or CI. This post walks through a four-step process 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
GROQ has no group-by, so start by listing every document type that actually has published documents. This runs in the Vision plugin or via the Sanity CLI:
// Run in Sanity Vision or via `sanity documents query`
array::unique(*[!(_id in path("drafts.**"))]._type)
Any type defined in your schema folder that doesn't appear in this list has zero published documents and is a candidate for removal (ignore system types such as sanity.imageAsset). For types that do appear, get a per-type count with count(*[_type == "pressRelease" && !(_id in path("drafts.**"))]) — or loop over the list in the script below — and cross-reference the results with the schema folder.
Interpret the Results
A typical breakdown on an older Studio falls into three buckets:
- Types with zero documents
- Types with only a handful of documents — often drafts that were never published
- Types actively used in production
The first two buckets are your candidates. 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.
A common case: a teamMember type has almost no standalone docs, but the few that exist are referenced from an aboutPage singleton editors update every week. Keep 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`)
}
Runtime depends on dataset size and the number of candidates. Log results to a JSON file and review them 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 --stats
The --stats flag prints stats about the built bundles. Save the output before and after the prune and compare chunk sizes. How much you save depends on what the removed types pulled in — plain field definitions are small, while types with custom inputs, previews, or heavy dependencies account for most of the difference.
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. A shared
linkFieldinput might be imported by the schema you're deleting and by a dozen active schemas. Deleting the schema won't break the Studio — but the shared input file and its dependencies have to stay.
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.
Mark the Type as Deprecated
// schemas/pressRelease.ts
import {defineType} from 'sanity'
export default defineType({
name: 'pressRelease',
type: 'document',
title: 'Press Release',
deprecated: {
reason: 'No longer used — kept so old documents stay editable',
},
readOnly: true, // optional: stop edits to old documents
fields: [
{name: 'title', type: 'string'},
// …
],
})
To also keep the type out of the "Create new document" menu and the Structure tool's lists, filter it out with document.newDocumentOptions in sanity.config.ts and leave it out of your custom structure.
What deprecating does:
- Shows editors a deprecation notice with your reason
- Old documents remain fully queryable via GROQ
- Existing content is preserved and unaffected
What deprecating 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
Record it on both a fast desktop connection and a throttled mobile profile, before and after each prune, so the numbers in your metrics file are comparable.
Use DevTools Coverage for Unused JavaScript
Open your Studio URL (https://yourproject.sanity.studio/) and use the Coverage panel in Chrome DevTools (Lighthouse's "Reduce unused JavaScript" audit gives a similar view). Types with custom inputs or previews are the ones most likely to ship code that every editor downloads 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
Example: a
legacyBlogPosttype can have zero documents in production but still be used in a staging dataset for ongoing QA testing. Deleting the schema would break the staging Studio for the QA team — even though production is 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 large, older Studio: a couple of hours, mostly spent on the reference checks
- Follow-up audits, once you have a baseline: much quicker, since you're only reviewing what changed
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
What to Expect From a Prune
The payoff scales with how much dead weight the Studio carries. Older projects with many abandoned types — especially ones with custom inputs, previews, or heavy third-party dependencies — see the biggest bundle and load-time drops. A young Studio with a handful of plain unused types will see little bundle change, but still benefits from a cleaner schema folder and less editor confusion. Measure with sanity build --stats and your metrics file rather than assuming a fixed percentage.
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) keeps the Studio lean and gives you measurable before/after numbers. 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.
Top comments (0)