Modal popup dialog example with onClose function in Sanity Studio

7 replies
Last updated: Sep 22, 2022
Does anyone have an example of a modal popup dialog in their Sanity Studio? I am specifically interested in your onClose function, but also interested in seeing what else I may want/need. TIA!
AI Update

I can provide you with examples of modal popup dialogs in Sanity Studio, particularly focusing on the onClose function!

Based on the Sanity UI documentation, here's how to implement a Dialog component properly:

Basic Setup

First, you'll need to set up the state and callback handlers:

import {useCallback, useState} from 'react'
import {Dialog, Card, Button, Box, Text} from '@sanity/ui'

const [open, setOpen] = useState(false)
const onClose = useCallback(() => setOpen(false), [])
const onOpen = useCallback(() => setOpen(true), [])

Complete Dialog Example

Here's a full working example with the Dialog component:

<Card padding={4} style={{textAlign: 'center'}}>
  <Button onClick={onOpen} text="Open dialog" />
</Card>

{open && (
  <Dialog
    header="Example Dialog"
    id="dialog-example"
    onClose={onClose}
    zOffset={1000}
  >
    <Box padding={4}>
      <Text>Dialog content goes here</Text>
    </Box>
  </Dialog>
)}

Key Properties You'll Want to Use

The Dialog component accepts several useful properties:

  • id (required): String identifier for the dialog
  • onClose: Callback function when dialog should close
  • header: React node for the dialog header
  • footer: React node for the dialog footer
  • width: Number or array for responsive widths
  • zOffset: Number to control z-index stacking
  • animate: Boolean to enable/disable animation on mount
  • scheme: 'dark' or 'light' color scheme

The onClose Function

The onClose function is crucial - it's called when:

  • User clicks outside the dialog
  • User presses the Escape key
  • User clicks a close button (if you add one)

The pattern useCallback(() => setOpen(false), []) is recommended to avoid unnecessary re-renders and ensures stable function references.

Here's a more complete example showing a confirmation dialog with action buttons:

{open && (
  <Dialog
    header="Confirm Action"
    id="confirm-dialog"
    onClose={onClose}
    footer={
      <Box padding={3}>
        <Flex gap={2} justify="flex-end">
          <Button text="Cancel" mode="ghost" onClick={onClose} />
          <Button text="Confirm" tone="primary" onClick={handleConfirm} />
        </Flex>
      </Box>
    }
  >
    <Box padding={4}>
      <Text>Are you sure you want to proceed?</Text>
    </Box>
  </Dialog>
)}

Document Action Example

If you're building custom document actions in Sanity Studio (which often use dialogs with type 'confirm'), here's an example pattern:

export const myCustomAction = (props) => {
  return {
    label: 'My Action',
    onHandle: () => {
      props.onComplete()
    },
    dialog: {
      type: 'confirm',
      message: 'Are you sure?',
      onConfirm: () => {
        // Do something
        props.onComplete()
      },
      onCancel: () => {
        props.onComplete()
      }
    }
  }
}

For more examples and interactive demos, check out the Dialog example in the Sanity UI Arcade where you can see it in action!

Show original thread
7 replies
Are you referring to the mechanics of it? There's a quick example in the UI docs that might be helpful: https://www.sanity.io/ui/docs/component/dialog
Hey
user R
! Is there some particular functionality you're looking for an example of?
well, i am just struggling to get my modal to close
This is my action.

export function PublishMicrositeAction(props) {
    if (props.type !== 'microsite') {
      return null
    }
    getMicrositeHook(props)
    return {
      label: "Publish Microsite",
      onHandle: () => {
      },
      dialog: {
          type: 'modal',
          onClose: (onClose function to go here),
          content: <div class="modal" style={{paddingBottom: 30 + 'px'}}>
          <h3>Publishing {siteName}</h3>
          <p>Please enter a brief description of what was changed.</p>
          <form action="${baseHookUrl}${hookId}"  method="POST" >
          <label for="trigger_title">Message: </label>
          <input type="text" name="trigger_title" id="trigger_title" />
          <button class="submit" type="button" style={{marginLeft: 10 + 'px'}}>Publish</button>
          </form>
          </div>

      }
    }

  }
Here's an example of a document action I created that creates a task document then closes itself:
import React, {useState, useEffect} from 'react'
import {client} from '../../../src/utils/sanityClient'
import {ComposeIcon} from '@sanity/icons'
import CreateTaskForm from './components/CreateTaskForm'
import {v4 as uuid} from 'uuid'

export function CreateTaskAction(props) {
  const [creating, setCreating] = useState(false)
  const [isDialogOpen, setIsDialogOpen] = useState(false)
  const [task, setTask] = useState({
    status: {
      isHighPriority: false,
      isBlocked: false,
      isComplete: false,
    },
    title: '',
    description: '',
    category: '',
    dueDate: new Date(),
    connections: [
      {
        _type: props.type,
        _ref: props.id,
      },
    ],
    contributors: false,
  })

  const handleClose = async () => {
    setCreating(true)

    const doc = {
      ...task,
      dueDate: task.dueDate?.toISOString(),
      contributors: task.contributors
        ? [
            {
              _type: 'contributor',
              _ref: task.createdBy._ref,
            },
          ]
        : [],
      _type: 'task',
      _id: uuid(),
    }

    await client
      .create(doc, {autoGenerateArrayKeys: true})
      .then((doc) => {
        setCreating(false)
        setTask({
          status: {
            isHighPriority: false,
            isBlocked: false,
            isComplete: false,
          },
          title: '',
          description: '',
          category: '',
          connections: [
            {
              _type: 'ticket',
              _ref: props.id,
            },
          ],
          contributors: false,
        })
        setIsDialogOpen(false)
      })
      .catch((err) => {
        console.log(err)
      })
  }

  return {
    label: 'Create Task',
    icon: <ComposeIcon />,
    onHandle: () => setIsDialogOpen(true),
    dialog: isDialogOpen && {
      type: 'modal',
      onClose: () => setIsDialogOpen(false),
      header: 'Create Task',
      content: (
        <CreateTaskForm
          handleClose={handleClose}
          setTask={setTask}
          task={task}
          creating={creating}
          setCreating={setCreating}
        />
      ),
    },
  }
}
That was very, very helpful! Thank you!
You're welcome!

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?