Implementation of document internationalization and organization of pages in a list item discussed.

8 replies
Last updated: May 19, 2022
Help me implement this: https://github.com/sanity-io/document-internationalization/blob/main/docs/known-caveats.md#usage-with-singletons
If I add that code to

export default () => S.list()
  .id('__root__')
  .title('Content')
  ..items([
    [add here?], <---- Throws error! Expected List item, found Object
    S.divider(),
    ...restOfTypes
  ])
AI Update

The URL you're trying to follow (known-caveats.md) doesn't actually exist in the document-internationalization repository—it returns a 404 error. However, the actual documentation for implementing singletons is in 01-singleton-documents.md.

The error "Expected List item, found Object" happens because you're trying to spread a single object directly into the items array. The fix is to use the spread operator (...) on the array returned by .map(), not on individual objects.

Here's the correct implementation from the actual singleton documentation:

import {StructureResolver} from 'sanity/structure'

// Define your singleton documents
const SINGLETONS = [
  {id: 'home', _type: 'page', title: 'Home'},
  {id: 'login', _type: 'page', title: 'Login'},
]

// Define your languages
const LANGUAGES = [
  {id: 'en', title: 'English'},
  {id: 'fr', title: 'French'},
]

export const structure: StructureResolver = (S) =>
  S.list()
    .title('Content')
    .items([
      // Spread the array returned by .map() - this is the key!
      ...SINGLETONS.map((singleton) =>
        S.listItem()
          .title(singleton.title)
          .id(singleton.id)
          .child(
            S.list()
              .title(singleton.title)
              .id(singleton.id)
              .items(
                LANGUAGES.map((language) =>
                  S.documentListItem()
                    .schemaType('page')
                    .id(`${singleton.id}-${language.id}`)
                    .title(`${singleton.title} (${language.id.toLocaleUpperCase()})`)
                )
              )
              .canHandleIntent(
                (intentName, params) => 
                  intentName === 'edit' && params.id.startsWith(singleton.id)
              )
          )
      ),
      S.divider(),
      // ...other list items
    ])

Why this works:

  1. SINGLETONS.map() returns an array of S.listItem() objects
  2. The spread operator ... unpacks that array into individual items
  3. Each singleton becomes a nested list showing all language versions

Before this will work, you need to:

  1. Create the actual singleton documents using the createSingletons.ts script. This creates both the language-specific documents (like home-en, home-fr) and the translation.metadata document that binds them together.

  2. Add a language field to your schema (usually hidden):

defineField({
  name: 'language',
  type: 'string',
  readOnly: true,
  hidden: true,
})

The canHandleIntent ensures that when you click on a singleton document from elsewhere in the Studio, it opens in the correct place in your structure.

The only thing that can be a direct descendent of
S.list()
is a
S.listItem()
. You'll need to add it before the
documentList
bit.
export default () => S.list()
  .id('__root__')
  .title('Content')
  ..items([
    S.listItem()
      .title('<your-title>')
      .child(
        S.documentList()
         .id('schemaType')
         .title('My Schema Type')
         .schemaType('schemaType')
         .filter('_id == $id && _type == $type')
         .params({
            id: 'schemaType',
            type: 'schemaType',
          })
  .menuItems([
    {
      title: 'Create new',
      intent: {
        type: 'create',
        params: {
          id: 'schemaType',
          type: 'schemaType',
          template: 'schemaType'
        }
      }
    }
  ]))
    S.divider(),
    ...restOfTypes
  ])
Thank you, Is Working!
Any way to group them?
Let say this is homePage.
Group singleton homePage, aboutPage on a list item called
Pages
?
Something like this?
Content
|_Pages
  |_homePage
  |_aboutPage
Or am I misunderstanding?
Yes. Applying same logic as above, with singletons
Ah, got it. In my studio I add them like this:
S.listItem()
      .title('Sandbox')
      .icon(MdOutlineBugReport)
      .child(S.document().schemaType('sandbox').documentId('sandbox')),
    S.listItem()
      .title('Custom Input Examples')
      .icon(MdCode)
      .child(
        S.document()
          .schemaType('allInputExamples')
          .documentId('allInputExamples')
      ),
    S.divider(),
Have you tried this solution of switching locales? For base locale, it shows perfect, however changing locales shows nothing.
I'm thinking to tweak the first code you gave me:

• Change the GROQ filter to show a list of ID's (homePage, aboutPage), not just one.
• Creating specific menu item for each page, like Create HomePage , Create AboutPage .
Made it work with tweaks above. Thank you for helping me 🙌
Awesome! Glad you got it working!

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.

Was this answer helpful?