📦 Out of the box and into your content operations: explore the Spring Release →

Create a new reference document with parameterised initial Values

By Saskia Bobinska

If you want to make it possible to use parameterised initialValue templates in reference fields, this is how!

CreateNewRefInput.tsx

import { AddIcon } from '@sanity/icons'
import { Button, Flex } from '@sanity/ui'
import { uuid } from '@sanity/uuid'
import { useCallback, useMemo } from 'react'
import {
  isNonNullable,
  pathToString,
  ReferenceInputProps,
  set,
  setIfMissing,
} from 'sanity'
import { useRouter, useRouterState } from 'sanity/router'
import { RouterPanes } from 'sanity/structure'

import { useFormValue } from 'sanity'
import styled from 'styled-components'

const CreateNewRefInput = (props: ReferenceInputProps) => {
  // in this example we use the language field value as the params later
  const language = useFormValue(['language']) as string

  const { weak, name } = props.schemaType

  const { navigate } = useRouter()
  const routerState = useRouterState()

  const routerPaneGroups = useMemo<RouterPanes>(
    () => (routerState?.panes || []) as RouterPanes,
    [routerState?.panes],
  )

  const openPane = (newDocumentId: string) => {
    const nextPanes: RouterPanes = [
      ...routerPaneGroups,
      [
        {
          id: newDocumentId,
          params: {
            type: 'page',
            template: 'YOUR_TEMPLATE_ID',
            parentRefPath: pathToString(props.path),
          },
          // Pass down values to the new document here
          payload: {
            language: language
          },
        },
      ],
    ]

    navigate({
      panes: nextPanes,
    })
  }

  const newDocumentId = uuid()

  const handleCreateNew = useCallback(() => {
    const patches = [
      setIfMissing({}),
      set(name, ['_type']),
      set(newDocumentId, ['_ref']),
      set(true, ['_weak']),
      // This is where you would set the template ID and would also need to make sure the right types are passed down
      set({ type: 'page', weak: weak, template: 'YOUR_TEMPLATE_ID' }, [
        '_strengthenOnPublish',
      ]),
    ].filter(isNonNullable)

    props.onChange(patches)

    openPane(newDocumentId)
    props.onPathFocus([])
  }, [props.onChange, props.onPathFocus, weak, name])

  return (
    <div>
      <Flex gap={2}>
        <FullWidth>{props.renderDefault(props)}</FullWidth>
        {/* You can also add the "Add new" tooltip like this https://www.sanity.io/ui/docs/primitive/tooltip */}
        <Button
          icon={AddIcon}
          mode="ghost"
          text={'Create New Page'}
          onClick={() => handleCreateNew()}
        />
      </Flex>
    </div>
  )
}
const FullWidth = styled.div`
  width: 100%;
`
export default CreateNewRefInput

Add the CreateNewRefInput to your reference field

    defineField({
      type: 'reference',
      name: 'reference',
      title: 'Reference',
      to: [{type: 'REFERENCED_DOC_TYPE'}],
      components: {
        input: CreateNewRefInput,
      },
      options: {
        disableNew: true,
      },
    }),

define your initialValue template in your sanity.config.ts

    {
      id: 'TEMPLATE_USED',
      title: 'This is a template',
      description: '...',
      schemaType: 'REFERENCED_DOC_TYPE',
      parameters: [{name: 'language', type: 'string'}],
      value: (params) => ({
        language: params.language,
      }),
    },

workaround with IntentLink, if you want a public API

<IntentLink
  intent="create"
  params={[
    { type: 'REFERENCED_DOC_TYPE', template: 'TEMPLATE_USED' },
    { language: language },
  ]}
>
  <Button icon={AddIcon} mode="ghost" />
</IntentLink>

Using the Create New button in reference fields will only allow you to use initialValue templates but not add any params to the template.

In order to be able to, for example, use a value from the document the reference field is used in, you need to set the reference option disableNew: true and add a custom input component that uses the same logic as the default reference input component.

Please note that certain elements imported from Sanity are considered internal and private. As such, they may change at any time without notice, and you'll need to handle any resulting issues on your own.
If you run into problems, the open-source Sanity repository can be a helpful resource for troubleshooting.

To circumvent this you can also use a parameterised IntentLink, but this will not give you the same default input behaviour.

Contributor

Other schemas by author