Discussion about creating custom roles and permissions in Sanity for a non-profit client.

7 replies
Last updated: Oct 7, 2021
Hi guys I have a question about Sanity's roles and plans. I have used Sanity for a number of projects and really love how flexible it makes the websites I build, however it seems surprisingly rigid in this area. I have a new build coming up for client who requires a few different levels of permissions within the studio when editing content.
• Administrator (can edit everything)
• Editor (can create, edit and publish documents)
• Author (can edit and publish certain documents but can't edit navigation or add new pages)
They have also asked for users to be able to be limited to certain areas of the cms.

It seems like creating these roles and the relevant permissions might be possible using Sanity's
Roles but I was surprised to see this functionality is limited to the enterprise plan. The client is a not for profit and are ok paying $99p/m but there's no way they'll be able to afford over $1000 p/m. I'm hoping there's a way around this (I've been looking at workflows ?) as it would be a deal breaker for the client, which is a shame as Sanity will be so perfect for the rest of the site.
AI Update

I totally understand your frustration - this is one of the most common pain points I see people hit with Sanity. The good news is there are several workarounds you can use to achieve role-based permissions without the Enterprise plan, though they require a bit more custom code.

What's Available on Different Plans

You're right that custom roles are limited to Enterprise. On the Growth plan ($99/month), you get:

  • Up to 5 default role assignments (Administrator, Developer, Editor, Viewer, Contributor)
  • Up to 50 user seats

These default roles are fairly broad, so they won't give you the granular control your client needs out of the box.

Practical Workarounds

Here are the approaches I'd recommend for your use case:

1. Hide UI Elements Based on Role Using Structure Builder

You can customize the Studio UI to hide document types, actions, and navigation items based on the current user's role. This doesn't enforce permissions at the API level, but it prevents users from accessing things they shouldn't in the Studio interface.

The Structure Builder API provides access to currentUser in its context, which you can use to filter what appears:

import type {StructureResolver} from 'sanity/structure'

export const structure: StructureResolver = (S, context) => {
  const user = context.currentUser
  const isAdmin = user?.roles.some(role => role.name === 'administrator')
  
  return S.list()
    .title('Content')
    .items([
      S.documentTypeListItem('page').title('Pages'),
      
      // Only show navigation to admins and editors
      ...(isAdmin || user?.roles.some(r => r.name === 'editor')
        ? [S.documentTypeListItem('navigation').title('Navigation')]
        : [])
    ])
}

2. Customize Document Actions

You can customize document actions to control who can publish based on their role. This is done in your sanity.config.ts:

import {defineConfig} from 'sanity'

export default defineConfig({
  // ... other config
  document: {
    actions: (prev, context) => {
      const {currentUser, schemaType} = context
      const isAuthor = currentUser?.roles.some(r => r.name === 'editor')
      
      // Authors can't publish navigation
      if (schemaType === 'navigation' && isAuthor) {
        return prev.filter(action => action.action !== 'publish')
      }
      
      return prev
    }
  }
})

3. Control Document Creation with Initial Value Templates

You can hide or show document creation templates based on user roles. The Studio customizations lesson shows how to control both initial value templates and the "Create +" menu:

export default defineConfig({
  // ... other config
  schema: {
    types: schemaTypes,
    templates: (prev, context) => {
      const {currentUser} = context
      const isAuthor = currentUser?.roles.some(r => r.name === 'editor')
      
      if (isAuthor) {
        // Filter out page creation for authors
        return prev.filter(template => template.id !== 'page')
      }
      
      return prev
    }
  },
  document: {
    newDocumentOptions: (prev, {currentUser}) => {
      const isAuthor = currentUser?.roles.some(r => r.name === 'editor')
      
      if (isAuthor) {
        // Remove page from the global create menu
        return prev.filter(item => item.templateId !== 'page')
      }
      
      return prev
    }
  }
})

4. Use Conditional Fields

You can make fields hidden or read-only based on role within your schema:

defineField({
  name: 'slug',
  type: 'slug',
  readOnly: (context) => {
    const isAdmin = context.currentUser?.roles.some(r => r.name === 'administrator')
    return !isAdmin
  }
})

About Workflows

The Workflows plugin you mentioned is more about content review and approval processes rather than restricting access to specific content areas. It won't solve your role-based access control needs, though it could complement the above approaches for editorial workflows.

Important Limitations

These workarounds have a key limitation: they only restrict the Studio UI, not the API. A technically savvy user could still use the API directly to edit restricted content. For most clients (especially non-profits with trusted staff), this is an acceptable trade-off. True API-level permission enforcement with GROQ filters and content resources requires Enterprise.

My Recommendation

For your non-profit client, I'd implement a combination of the approaches above:

  1. Use Structure Builder to hide navigation from Authors
  2. Customize document actions to remove publish capabilities where needed
  3. Control document creation through templates and newDocumentOptions
  4. Use conditional fields for additional granularity

This will give you:

  • Administrators who see and can edit everything
  • Editors who can create/edit/publish most documents including navigation
  • Authors with limited UI access to specific document types, no navigation access, and restricted page creation

The implementation takes some custom code, but it's totally doable and will give your client the experience they need within their budget. The Studio customizations lesson has comprehensive examples of these patterns in action, including filtering document lists, customizing actions, and controlling document creation.

If the client absolutely requires API-level security (for example, if they have untrusted users or compliance requirements), then you'd need to either advocate for Enterprise pricing or consider whether Sanity is the right fit for this specific project. But for most real-world scenarios with trusted team members, the UI-level restrictions work great and are widely used in production.

Hey Rachel, ah yes, that's awesome thank you, I'll apply for it. I couldn't see anything about whether it included Custom Roles though?
Oh, actually, you're right. There are no custom roles included in the non-profit plan. One option to avoid this would be to render a different custom desk structure for the editors that you only want to be able to access certain documents. You'd have to build off of something like this and get the current user from the userStore to limit the structure further. There's an example of using the userStore in this initial value template example . Note that these users would still be able to access all documents through the API or searchbar, though.
Ah yep, cool I was looking at something like this. I was thinking I'd also be able to customise the document actions for certain users too? In order to limit their ability to create new pages, but still allow them to edit existing ones?
With regards to the search bar - is it possible to just turn that off for some users?
Ah yep, cool I was looking at something like this. I was thinking I'd also be able to customise the document actions for certain users too? In order to limit their ability to create new pages, but still allow them to edit existing ones?
With regards to the search bar - is it possible to just turn that off for some users?
You may be able to hack-ily hide the search bar using CSS overrides . But It can be a pain to find the right thing to target. With limiting new pages, you can configure the new document menu to only show specific types. A custom document action that prevents certain users from publishing might be an option as well!
You may be able to hack-ily hide the search bar using CSS overrides . But It can be a pain to find the right thing to target. With limiting new pages, you can configure the new document menu to only show specific types. A custom document action that prevents certain users from publishing might be an option as well!
Ok cool – thanks very much Rachael, I think this will cover what I need.

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?