How to programmatically populate fields array using GROQ query data?

18 replies
Last updated: May 5, 2022
Hello!Is there a way to propagate the
fields
array programatically? I am trying to use the data returned from a
groq
query from another document to then create the needed fields.
InitialValue
seems to only set the state of already existing field and I don’t think a
reference
would work. Or if it could I am not sure how. Thanks!
AI Update

I understand what you're trying to do, but unfortunately Sanity schemas must be static and cannot be dynamically generated at runtime based on data. The fields array in a schema definition needs to be defined when the Studio is built/loaded, not dynamically created per document.

Here's why and what alternatives you have:

Why Schemas Must Be Static

Sanity Studio needs to know the complete schema structure upfront to:

  • Generate the editing interface
  • Provide proper validation
  • Enable GROQ queries to work correctly
  • Support TypeScript type generation

You're correct that initialValue only sets the values of existing fields - it doesn't create new fields. And while references link to other documents, they don't dynamically alter the schema structure itself.

Alternative Approaches

Depending on what you're trying to accomplish, here are some solutions:

1. Use Conditional Fields

If you need fields to appear/disappear based on document data, use the hidden property:

defineField({
  name: 'conditionalField',
  type: 'string',
  hidden: ({document}) => document?.someField !== 'specificValue'
})

2. Use Objects or Arrays for Flexible Structure

For truly dynamic data structures, use an array of objects where users can add different field combinations:

defineField({
  name: 'dynamicContent',
  type: 'array',
  of: [
    {type: 'textBlock'},
    {type: 'imageBlock'},
    {type: 'customBlock'}
  ]
})

3. Use Initial Value Templates for Dynamic Defaults

While you can't create fields dynamically, you can use Initial Value Templates to fetch data from other documents and populate field values when creating new documents:

{
  id: 'document-from-template',
  title: 'New Document',
  schemaType: 'myType',
  value: async (params, context) => {
    const client = context.getClient({apiVersion: '2024-11-01'})
    const sourceData = await client.fetch(
      `*[_type == "source" && _id == $id][0]`,
      {id: params.sourceId}
    )
    return {
      field1: sourceData.someValue,
      field2: sourceData.anotherValue
      // These fields must already exist in your schema
    }
  }
}

4. Use a Key-Value Pattern

For truly arbitrary data, use an array of key-value pairs:

defineField({
  name: 'metadata',
  type: 'array',
  of: [{
    type: 'object',
    fields: [
      {name: 'key', type: 'string'},
      {name: 'value', type: 'string'}
    ]
  }]
})

5. Multiple Schemas Based on Context

If you need completely different field sets, create separate document types and use Initial Value Templates to guide users to the right one based on context.

If you can share more about your specific use case (what kind of data you're trying to pull and why you need it to generate fields), I can suggest a more tailored approach!

Show original thread
18 replies
I think the canonical way to resolve this at the moment is to have all possible fields defined in the schema, and use the hidden and read only properties defined dynamically based on the groq queries you mentioned.
user P
thanks for the quick reply. That is also what I am finding. But
hidden
can not return a
promise
which is required for the
groq
. Any work arounds?
I haven’t had the problem you’re describing as I usually believe it
Can the
hidden
callback access all the documents or only the parent?
I haven’t had the problem you’re describing as I usually believe it’s a sign that I should either• A - Use the block field for a dynamic content, or an array of various fields that the user can pick from.
• B - Create a new schema for the desired purpose.
With that said, I would look into initialValueTemplates - but without intimately understanding why you’re needing this I’m not sure I can help
Maybe it will help to describe the problem. 😉
One document has a list of languages the user can select. This value is an array of strings.

Then a custom
object
type would use the value from the previous document to dynamically render the selected languages input elements for the Google Translate plugin.
It could be a filter solution maybe? But the props from
hidden
only give you the document values from the current document so hidden is not an option
Could you help me understand the relationship between these two documents and why they are seperate?
Sure.
The
object
is a reusable schema that creates inputs for the Google Translate plugin. It is a custom
type
. Must be separate.
The other is a basic
document
with elements of
type
string
which uses the above
object type
as their
types
Here are some snipets.
Document

fields: [
    {
      name: 'title',
      title: 'Heder Title',
      type: 'localizedString',
      description: 'Page header title'
    },
...],
Custom Google Translate Object Type

const localizedString = {
  name: 'localizedString',
  type: 'object',
  inputComponent: GoogleTranslateInput,
  options: {
    apiKey: process.env.SANITY_STUDIO_GOOGLE_CLOUD_API_KEY
  },
  fieldsets: [
    {
      title: 'Translations',
      name: 'translations',
      options: { 
collapsible: true, collapsed: true }
    }
  ],
  fields: allLanguages.map((lang) => ({
    type: 'string',
    fieldset: lang.value === 'en' ? null : 'translations',
    options: {
      list: async () => {
        const selectedLangs = await languages();
        return allLanguages.map((lang) => !selectedLangs.includes(lang.value))
      },
      layout: 'string'
    },
  }))
};
The
const selectedLangs = await languages();
is calling a helper function that is the
groq
query
I haven’t ghosted you, in a standup until the top of the hour. will look back when I can focus on it.
No worries
user P
I appreciate it.
So I think this plugin maybe the best way to approach my problem. At least is helps depict what I am looking for better. But I was looking to just have a way of doing it thru a separate document for the languages

https://www.npmjs.com/package/@sanity/language-filter
Ahh, would you still like help or is this resolving your challenge?
Well this plugin doc are not great so lets see. 😉
But I think I am ok for now. Thanks!

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?