Skip to main content

Custom UI

You can display custom user interface (UI) components using the @metamask/snaps-sdk module with the following methods and entry points:

To use custom UI, first install @metamask/snaps-sdk using the following command:

yarn add @metamask/snaps-sdk

Then, whenever you're required to return a custom UI component, import the components from the SDK and build your UI with them. For example, to display a panel using snap_dialog:

index.js
import { panel, heading, text } from "@metamask/snaps-sdk";

await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: panel([
heading("Alert heading"),
text("Something happened in the system."),
]),
},
});

Components

The following custom UI components are available:

address

Outputs a formatted text field for an Ethereum address. The address is automatically displayed with a jazzicon and truncated value. Hovering the address shows the full value in a tooltip.

Example

index.js
import { panel, heading, address } from "@metamask/snaps-sdk";

await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: panel([
heading("Are you sure you want to send tokens to this address?"),
address("0x000000000000000000000000000000000000dEaD"),
]),
},
});
Address UI example
Address tooltip UI example

button

Flask Only
This feature is experimental and only available in MetaMask Flask, the canary distribution of MetaMask.

Outputs a button that the user can select. For use in interactive UI.

Parameters

An object containing:

  • value: string - The text of the button.
  • buttonType: string - (Optional) Possible values are button or submit. The default is button.
  • name: string - (Optional) The name that will be sent to onUserInput when a user selects the button.
  • variant - (Optional) Determines the appearance of the button. Possible values are primary or secondary. The default is primary.

Example

import { button, panel, heading } from "@metamask/snaps-sdk";

const interfaceId = await snap.request({
method: "snap_createInterface",
params: {
ui: panel([
heading("Interactive interface"),
button({
value: "Click me",
name: "interactive-button",
}),
]),
},
});

await snap.request({
method: "snap_dialog",
params: {
type: "Alert",
id: interfaceId,
},
});

Button UI example

copyable

Outputs a read-only text field with a copy-to-clipboard shortcut.

Example

index.js
import { text, copyable } from "@metamask/snaps-sdk";

await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: panel([
text("Your address:"),
copyable("0x000000000000000000000000000000000000dEaD"),
]),
},
});

Copyable UI example

divider

Outputs a horizontal divider.

Example

index.js
import type { OnHomePageHandler } from "@metamask/snaps-sdk";
import { panel, divider, text } from "@metamask/snaps-sdk";

module.exports.onHomePage = async () => {
return {
content: panel([
heading("Hello world!"),
divider(),
text("Welcome to my Snap home page!"),
]),
};
};

Divider UI example

form

Flask Only
This feature is experimental and only available in MetaMask Flask, the canary distribution of MetaMask.

Outputs a form for use in interactive UI.

Parameters

An object containing:

  • name: string - The name that will be sent to onUserInput when a user interacts with the form.
  • children: array - An array of input or button components.

Example

import { input, button, form } from "@metamask/snaps-sdk";

const interfaceId = await snap.request({
method: "snap_createInterface",
params: {
ui: form({
name: "form-to-fill",
children: [
input({
name: "user-name",
placeholder: "Your name",
}),
button({
value: "Submit",
buttonType: "submit",
}),
],
}),
},
});

await snap.request({
method: "snap_dialog",
params: {
type: "Alert",
id: interfaceId,
},
});

Form UI example

heading

Outputs a heading. This is useful for panel titles.

Example

index.js
import type { OnHomePageHandler } from "@metamask/snaps-sdk";
import { panel, heading, text } from "@metamask/snaps-sdk";

module.exports.onHomePage = async () => {
return {
content: panel([
heading("Hello world!"),
text("Welcome to my Snap home page!"),
]),
};
};

Divider UI example

image

Outputs an image. This component takes an inline SVG. It does not support remote URLs.

You can import SVG, PNG, and JPEG files using an import statement. These files are automatically imported as SVG strings, so you can pass them directly to the image component.

The SVG is rendered within an <img> tag, which prevents JavaScript or interaction events from being supported.

note

To disable image support, set the features.images configuration option to false. The default is true.

Example

index.js
import type { OnHomePageHandler } from "@metamask/snaps-sdk";
import { panel, heading, text, image } from "@metamask/snaps-sdk";
import svgIcon from "./path/to/icon.svg";

module.exports.onHomePage = async () => {
return {
content: panel([
heading("Hello world!"),
text("Welcome to my Snap home page!"),
image(svgIcon),
]),
};
};

Divider UI example

input

Flask Only
This feature is experimental and only available in MetaMask Flask, the canary distribution of MetaMask.

Outputs an input component for use in interactive UI.

Parameters

An object containing:

  • name: string - The name that will be used as a key to the event sent to onUserInput when the containing form is submitted.
  • inputType: string - (Optional) Type of input. Possible values are text, number, or password. The default is text.
  • placeholder: string - (Optional) The text displayed when the input is empty.
  • label: string (Optional) The text displayed alongside the input to label it.
  • value: string (Optional) The default value of the input.

Example

import { input, form } from "@metamask/snaps-sdk";

const interfaceId = await snap.request({
method: "snap_createInterface",
params: {
ui: form({
name: "form-to-fill",
children: [
input({
name: "user-name",
placeholder: "Your name",
}),
button({
value: "Submit",
buttonType: "submit",
}),
],
}),
},
});

await snap.request({
method: "snap_dialog",
params: {
type: "Alert",
id: interfaceId,
},
});

Form UI example

note

See the @metamask/images-example-snap package for a full example of implementing images.

panel

Outputs a panel, which can be used as a container for other components. This component takes an array of custom UI components.

Example

index.js
import type { OnTransactionHandler } from "@metamask/snaps-sdk";
import { panel, heading, text } from "@metamask/snaps-sdk";

module.exports.onTransaction = async ({ transaction }) => {
const gasFeesPercentage = /* Calculate gas fees percentage */;
return {
content: panel([
heading("Transaction insights"),
text(
`As set up, you are paying **${gasFeesPercentage.toFixed(
2,
)}%** in gas fees for this transaction.`,
),
]),
};
};

Panel UI example

row

Outputs a row with a label and value, which can be used for key-value data. The label must be a string. The value can be a child component of type text or address.

Example

index.js
import { panel, row, text, address } from "@metamask/snaps-sdk";

await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: panel([
row("Address", address("0x000000000000000000000000000000000000dEaD")),
row("Balance", text("1.78 ETH")),
]),
},
});

Row UI example

spinner

Outputs a loading indicator.

Example

index.js
import { panel, heading, spinner } from "@metamask/snaps-sdk";

await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: panel([
heading("Please wait..."),
spinner(),
]),
},
});

Spinner UI example

text

Outputs text.

Example

index.js
import type { OnHomePageHandler } from "@metamask/snaps-sdk";
import { panel, heading, text } from "@metamask/snaps-sdk";

module.exports.onHomePage = async () => {
return {
content: panel([
heading("Hello world!"),
text("Welcome to my Snap home page!"),
]),
};
};

Text UI example

Markdown

text components accept bold and italic inline Markdown.

Example

index.js
import { panel, heading, text } from "@metamask/snaps-sdk";

await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: panel([
heading("Hello world!"),
text("This is **bold** and this is _italic_."),
]),
},
});

Markdown UI example

text components accept inline links.

Example

index.js
import type { OnHomePageHandler } from "@metamask/snaps-sdk";
import { panel, text } from "@metamask/snaps-sdk";

module.exports.onHomePage = async () => {
return {
content: panel([
heading("Hello world!"),
text("Download [MetaMask](https://metamask.io)."),
text("Read the MetaMask docs at [](https://docs.metamask.io)."),
]),
};
};

Links UI example

Emojis

Text-based components (such as heading and text) accept emojis.

Example

index.js
import { panel, heading, text } from "@metamask/snaps-sdk";

await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: panel([
heading("Hello world!"),
text("This is an apple 🍎 and this is an orange 🍊."),
]),
},
});

Emojis UI example

Examples

See the following packages for full examples of implementing custom UI: