Studio loses track of path when creating new document with custom deskStructure
This is a classic issue with custom desk structures in Sanity Studio! When you create a new document and the Studio "loses track" of where you are (jumping back to the root or wrong pane), it's almost always because your custom structure is missing the canHandleIntent method on your list items.
The Problem
When you create a new document, Sanity Studio tries to figure out which pane should handle that intent (like "create" or "edit"). Without canHandleIntent defined, the Studio can't match the new document to your custom filtered list items, so it falls back to opening the document in an unexpected location—usually the root pane.
The Solution
You need to add the canHandleIntent method to your custom listItem() definitions. This tells the Studio "yes, this pane can handle documents of this type with these characteristics."
Here's an example for a multilingual structure:
S.listItem()
.title('English Content')
.id('english-content')
.child(
S.documentList()
.title('English Content')
.filter('_type == $type && language == $language')
.params({ type: 'page', language: 'en' })
)
.canHandleIntent((intentName, params) => {
// Handle create and edit intents for documents with English language
return (
(intentName === 'create' || intentName === 'edit') &&
params.type === 'page' &&
params.language === 'en'
)
})Key Points for Language-Based Structures
For multilingual setups specifically, your canHandleIntent should check:
- Intent type - usually
'create'or'edit' - Schema type - the document type being created/edited (available as
params.type) - Language parameter - match it to your filtered list (available as
params.languageif you're setting it via initial value templates)
If you're using language folders, each language-specific list item needs its own canHandleIntent that checks for matching language values.
Complete Pattern
Here's a more complete pattern for a language-based structure:
const languageList = (lang: string) =>
S.listItem()
.title(`${lang.toUpperCase()} Content`)
.id(`${lang}-content`)
.child(
S.documentList()
.title(`${lang.toUpperCase()} Content`)
.filter('_type in $types && language == $lang')
.params({ types: ['page', 'post'], lang })
)
.canHandleIntent((intentName, params) => {
return (
['create', 'edit'].includes(intentName) &&
params.language === lang
)
})
export const structure: StructureResolver = (S) =>
S.list()
.title('Content')
.items([
languageList('en'),
languageList('es'),
languageList('fr'),
// ...other language lists
])Important Notes
- Make sure the parameters you check in
canHandleIntentmatch what's actually on your documents or what's being passed through initial value templates - If you're not setting a language field when creating documents, you may need to use initial value templates that set the language parameter
- The
id()on your list items is also important—make sure each has a unique, descriptive ID
Debugging Tips
If it's still not working after adding canHandleIntent:
- Check your initial value templates to ensure they're passing the correct parameters (especially
language) - Console log the
paramsobject insidecanHandleIntentto see what's actually being passed - Verify that your document schema has a
languagefield that matches what you're filtering on
The Structure Builder API reference has more details on all available methods. The canHandleIntent method is documented under both List and ComponentView sections, though examples can be tricky to find since it's often overlooked until you hit this exact issue!
Show original thread6 replies
Sanity – Build the way you think, not the way your CMS thinks
Sanity is the developer-first content operating system that gives you complete control. Schema-as-code, GROQ queries, and real-time APIs mean no more workarounds or waiting for deployments. Free to start, scale as you grow.