Can Form Component access parent component props/data in Sanity?

3 replies
Last updated: Jan 11, 2023
TL;DR: is it possible for a Form Component to access props/data from its parent component?
Hello! I’m using Sanity `palette`
image metadata on my website’s front-end, to add some colour to the page. I’ve added a select box to my schema, which allows users to select one of the available colours.
I’d like to display a small colour swatch beside the select box, and I’m attempting to use the
Form Components API to achieve this. So far, I’ve been able to gain access to the currently-selected value (see screenshot, below), but is it possible to access the image itself? I need to fetch the corresponding colour from the image’s
palette
metadata in order to render my colour swatch.
My colour palette field is a child of the image field, so I’m hoping it can access its parent somehow. My schema looks similar to this:


export default defineType({
  title: "Post",
  name: "post",
  type: "document",
  fields: [
    defineField({
      title: "Main image",
      name: "image",
      type: "image",
      fields: [
        defineField({
          title: "Colour palette",
          name: "colorPalette",
          type: "string",
          options: {
            list: [
              { title: "Dominant", value: "dominant" },
              { title: "Vibrant", value: "vibrant" },
              { title: "Light Vibrant", value: "lightVibrant" },
              { title: "Dark Vibrant", value: "darkVibrant" },
              { title: "Muted", value: "muted" },
              { title: "Light Muted", value: "lightMuted" },
              { title: "Dark Muted", value: "darkMuted" },
              { title: "Custom", value: "custom" }
            ]
          }
        })
      ]
    })
  ]
});
Thanks!
AI Update

Yes! You can access parent field data in your Form Component using the useFormValue hook. Since your colorPalette field is nested inside the image field, you can access the parent image data (including its palette metadata) by using the appropriate path.

Here's how to do it:

import { useFormValue } from 'sanity'
import { StringInputProps } from 'sanity'

function ColorPaletteInput(props: StringInputProps) {
  const { renderDefault } = props
  
  // Get the parent image object (including asset with palette metadata)
  const imageValue = useFormValue(['image'])
  
  // Access the currently selected color palette option
  const selectedPalette = props.value
  
  // Extract the color from the image's palette metadata
  const paletteColor = imageValue?.asset?.metadata?.palette?.[selectedPalette]?.background
  
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
      {paletteColor && (
        <div
          style={{
            width: '24px',
            height: '24px',
            borderRadius: '4px',
            backgroundColor: paletteColor,
            border: '1px solid #ccc'
          }}
        />
      )}
      {renderDefault(props)}
    </div>
  )
}

// In your schema:
defineField({
  title: "Colour palette",
  name: "colorPalette",
  type: "string",
  components: {
    input: ColorPaletteInput
  },
  options: {
    list: [
      { title: "Dominant", value: "dominant" },
      { title: "Vibrant", value: "vibrant" },
      // ... rest of your options
    ]
  }
})

The key insight is that useFormValue accepts a path array to access any field in the current document. Since you're inside a nested field, using ['image'] will give you access to the entire parent image object, including its asset.metadata.palette data.

The image palette metadata contains properties like background, foreground, population, and title for each color swatch (dominant, vibrant, lightVibrant, darkVibrant, muted, lightMuted, darkMuted).

Note that in Studio v3, accessing parent values requires useFormValue rather than the old v2 pattern of using props.parent or props.document.

Show original thread
3 replies
So, I'm getting closer. Instead of trying to read the props of the parent component, I discovered that I can access a component's children via
props.members
. I now have this in my `sanity.config.jsx`:
form: {
  components: {
    input: props =>
      props.schemaType?.title === "Main image" ? (
        <CustomInput {...props} />
      ) : (
        props.renderDefault(props)
      )
  }
}
With this component:

import { getImageAsset } from "@sanity/asset-utils";

function CustomInput(props) {
  const image = getImage(props.value.asset, { dataset, projectId });

  const selectedColor = props.members?.find(m => m.key === "field-colorPalette")?.field?.value;

  console.log("selectedColor", selectedColor);
  console.log("image", image);
  
  return (
    <Stack space={3}>
      {props.renderDefault(props)}
    </Stack>
  );
}
I can see the selected colour in my
console.log
, but the image asset doesn't contain a
metadata.palette
property - only
metadata.dimensions
.
Something like this should work for you:
const ListWithSwatch = (props) => {
  const {value, renderDefault} = props
  const [palette, setPalette] = useState()
  const [swatchColor, setSwatchColor] = useState()
  const image = useFormValue(['image']).asset._ref
  const client = useClient({apiVersion: '2023-01-10'})

  useEffect(() => {
    const getPallete = () =>
      client.fetch(`*[_id == $id][0].metadata.palette`, {id: image}).then(setPalette)
    getPallete()
  }, [image])

  useEffect(() => {
    palette && setSwatchColor(palette[value].background)
  }, [value, palette])

  return (
    <Inline space={3}>
      <Avatar style={{backgroundColor: swatchColor || 'fff'}} />
      {renderDefault(props)}
    </Inline>
  )
}
Amazing! Thanks,
user M
! This is great!

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?