Dialogs
You can display a dialog in the MetaMask UI using the
snap_dialog API method.
Dialogs can contain custom UI and interactive UI components.
There are four types of dialogs: alerts, confirmations, prompts, and custom dialogs.
Dialogs do not work when MetaMask is locked.
To check if MetaMask is locked, use
snap_getClientStatus.
Request permission to display dialogs
To display dialogs, first request the snap_dialog permission.
Add the following to your Snap's manifest file:
"initialPermissions": {
  "snap_dialog": {}
}
Display an alert dialog
To display an alert that can only be acknowledged, call
snap_dialog with type: "alert".
The following example displays custom UI that alerts the user when something happens in the system:
import { Box, Text, Heading } from "@metamask/snaps-sdk/jsx";
await snap.request({
  method: "snap_dialog",
  params: {
    type: "alert",
    content: (
      <Box>
        <Heading>Something happened in the system</Heading>
        <Text>The thing that happened is...</Text>
      </Box>
    ),
  },
});
// Code that should execute after the alert has been acknowledged.

Display a confirmation dialog
To display a confirmation that can be accepted or rejected, call
snap_dialog with type: "confirmation".
The following example displays custom UI that asks the user to confirm whether they would like to
take an action:
import { Box, Text, Heading } from "@metamask/snaps-sdk/jsx";
const result = await snap.request({
  method: "snap_dialog",
  params: {
    type: "confirmation",
    content: (
      <Box>
        <Heading>Would you like to take the action?</Heading>
        <Text>The action is...</Text>
      </Box>
    ),
  },
});
if (result === true) {
  // Do the action.
}

Display a prompt dialog
To display a prompt where the user can enter a text response, call
snap_dialog with type: "prompt".
Prompt dialogs also accept a placeholder value that displays in the input field when no text is entered.
The following example displays custom UI that prompts the user to enter a wallet address:
import { Box, Text, Heading } from "@metamask/snaps-sdk/jsx";
const walletAddress = await snap.request({
  method: "snap_dialog",
  params: {
    type: "prompt",
    content: (
      <Box>
        <Heading>What is the wallet address?</Heading>
        <Text>Please enter the wallet address to be monitored</Text>
      </Box>
    ),
    placeholder: "0x123...",
  },
});
// walletAddress will be a string containing the address entered by the user.

Display a custom dialog
To display a custom dialog, call snap_dialog
without providing a type. Custom dialogs can be resolved by calling snap_resolveInterface. The UI passed to a custom dialog should contain a Footer element. Its buttons will be displayed at the bottom of the dialog. Here is a complete example:
import {
  UserInputEventType,
  type OnRpcRequestHandler,
  type OnUserInputHandler,
} from "@metamask/snaps-sdk";
import {
  Box,
  Text,
  Heading,
  Container,
  Footer,
  Button,
} from "@metamask/snaps-sdk/jsx";
/**
 * Handle incoming JSON-RPC requests, sent through wallet_invokeSnap.
 *
 * @param args - The request handler args as object.
 * @param args.origin - The origin of the request, e.g., the website that
 * invoked the snap.
 * @param args.request - A validated JSON-RPC request object.
 * @returns The result of snap_dialog.
 * @throws If the request method is not valid for this snap.
 */
export const onRpcRequest: OnRpcRequestHandler = async () => {
  const result = await snap.request({
    method: "snap_dialog",
    params: {
      content: (
        <Container>
          <Box>
            <Heading>Custom Dialog</Heading>
            <Text>
              This is a custom dialog reproducing a confirmation dialog.
              <br />
              Do you accept?
            </Text>
          </Box>
          <Footer>
            <Button name="no">No</Button>
            <Button name="yes">Yes</Button>
          </Footer>
        </Container>
      ),
    },
  });
  console.log("result", result); // Result will be true or false.
  return result;
};
export const onUserInput: OnUserInputHandler = async ({ id, event }) => {
  if (event.type === UserInputEventType.ButtonClickEvent) {
    switch (event.name) {
      case "no": // User selected "No" in the footer.
        await snap.request({
          method: "snap_resolveInterface",
          params: {
            id,
            value: false,
          },
        });
        break;
      case "yes": {
        // User selected "Yes" in the footer
        await snap.request({
          method: "snap_resolveInterface",
          params: {
            id,
            value: true,
          },
        });
        break;
      }
      default:
        break;
    }
  }
};
This code outputs a custom dialog with two buttons: Yes and No.
When the user selects one of the buttons, onUserInput is called with the button's name. From there, snap_resolveInterface is called. This resolves the dialog, and returns the value passed to snap_resolveInterface as the result of the dialog.

Example
See the @metamask/dialog-example-snap
package for a full example of implementing dialogs.
This example exposes a custom JSON-RPC API
for dapps to display dialogs.