Migrating v2 StructureBuilder to v3: Access document types and icons in widgets

20 replies
Last updated: Jun 10, 2023
Hey everyone, I have been having issues with migrating a v2 edit content widget, it seems that the tool was using the desk tool structureBuilder and getting the icons from a list

import StructureBuilder from '@sanity/desk-tool/structure-builder';

export function getDefaultStructure() {
  const items = StructureBuilder.documentTypeListItems();
  return StructureBuilder.list()
    .id('__root__')
    .title('Content')
    .showIcons(items.some((item) => item.getSchemaType().icon))
    .items(items);
}
which later is used as in a React component file

{getDefaultStructure().items
          .filter((item) => item.type !== 'divider')
          .map((item) => {
            const Icon = getIconComponent(item);
            return (
              <div key={item.id}>
                <Link
                  className={styles.link}
                  href={`/${getEnvironmentName()}/desk/${item.id}`}>
                  <div className={styles.iconWrapper}>
                    <Icon />
                  </div>
                  <div>{item.title}</div>
                </Link>
              </div>
            );
          })}
Okay since there seems to be no way to access the StructureBuilder, which now seems only to exist inside the deskTool plugin, is there a way to retrieve it and use it in widgets? Or is there a GROQ query to get the different available document types used in sanity desk, with their respective icons. Thanks in advance.
AI Update

I understand your struggle! In Sanity v3 (and v4), the StructureBuilder is no longer directly importable as it was in v2. However, there's a better way to access document types and their icons in your custom widgets using the useSchema hook.

Using the useSchema Hook

The useSchema hook gives you access to all schema types in your Studio, including their icons. Here's how to adapt your code:

import { useSchema } from 'sanity'

function YourWidget() {
  const schema = useSchema()
  
  // Get all document types
  const documentTypes = schema.getTypeNames()
    .map(typeName => schema.get(typeName))
    .filter(type => type?.type === 'document')
  
  return (
    <div>
      {documentTypes.map((docType) => {
        const Icon = docType.icon
        return (
          <div key={docType.name}>
            <Link
              className={styles.link}
              href={`/${getEnvironmentName()}/desk/${docType.name}`}
            >
              <div className={styles.iconWrapper}>
                {Icon && <Icon />}
              </div>
              <div>{docType.title || docType.name}</div>
            </Link>
          </div>
        )
      })}
    </div>
  )
}

How It Works

  1. useSchema() - Returns the schema object with all registered types
  2. schema.getTypeNames() - Gets all type names in your schema
  3. schema.get(typeName) - Gets the full type definition including icon, title, name, etc.
  4. Filter for documents - Only show document types (not objects, arrays, etc.)

Accessing Icons from Structure Builder

If you specifically need to replicate the Structure Builder's default list items (which includes special handling and ordering), you can use the documentTypeListItems() method within your structure configuration, but you'll need to access it differently:

import { structureTool } from 'sanity/structure'

// In your sanity.config.js
export default defineConfig({
  plugins: [
    structureTool({
      structure: (S) => {
        // S is the Structure Builder instance
        const items = S.documentTypeListItems()
        // You can export or use this data here
        return S.list()
          .title('Content')
          .items(items)
      }
    })
  ]
})

However, for custom widgets outside the structure configuration, the useSchema hook approach is the recommended pattern in v3/v4.

Important Notes

  • The icon property from schema types is already a React component, so you can render it directly
  • Document types without an icon will have icon as undefined, so check before rendering
  • The order from getTypeNames() may differ from Structure Builder's default ordering, which alphabetizes and applies special rules

This approach gives you full access to document type metadata including icons, titles, and any other schema properties you need for your custom navigation widget!

Show original thread
20 replies
I’m afraid I can’t reproduce the v2 example you’ve provided to get a sense of what you’re expecting. Can you please provide some code that minimally reproduces what you’re after? Thanks!
Hey Geoff, thanks for getting back to me, basically I'm trying to get a similar widget for v3 on the dashboard
That's the finished widget, basically it uses StructureBuilder to get the different desk document types with their icons
I have seen something along the lines of createStructureBuilder but no documentation on how to use it
If you need more info I can zip the custom current plugin as is
Or if there is any example I'll be glad to take a look
Again thanks for your response
Okay an update I have been able to get all schema document types using useSchema, can I ask for v3 is there a new Link component
So I have disregarded using StructureBuilder
Great! Glad you’re on your way to a solution here.

I don’t believe
Link
is a component coming from the Studio. Could you check your v2 instance and see where it was imported from or defined?

Edit: Never mind. It’s defined by the Sanity Router.
It looks like
Link
remains the same in v3, though it may be imported differently than it was in v2. The component comes from the same location .
Okay thanks if this works out I'll publish a plugin for everyone, can I ask why is rxjs used in most Sanity plugins been looking through a lot of source code
And one last question is there a way to get the current location or base url of sanity or that is tied to useClient
RxJS Observables fill a gap that doesn’t yet exist in JavaScript. In the Studio context, they’re often quite valuable. As someone who doesn’t write plugins, I can’t really offer too good of a reason, but we make use of them throughout the Studio codebase, as well.
is there a way to get the current location or base url of sanity or that is tied to useClient
If you want the URL that the client is sourcing from, that’s available on
client.config()
.
If you want the current hostname where the client is being used, I’m not sure what best practice is but I believe I’ve commonly seen it inferred from
window
.
okay thanks geoff let me work on this regards
Hey Geoff, I'm getting the api url instead of the current location I'm using window.location.href is there a cleaner way?
Okay I'm looking at sanity withRouter component
I was hoping the with router exposes location history and all on the props so far no success
Found a work around sorry this is how do my thinking 🤣🤣🤣

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?