Custom UI with JSX
You can display custom user interface (UI) JSX components using the
@metamask/snaps-sdk
module when
implementing the following features:
To use custom UI with JSX, first install @metamask/snaps-sdk
using the following command:
yarn add @metamask/snaps-sdk
Then, whenever you're required to return a custom UI JSX component, import the components from the
SDK at @metamask/snaps-sdk/jsx
and build your UI with them.
For example, to display a Box
(the panel
function equivalent) using snap_dialog
:
import { Box, Heading, Text } from '@metamask/snaps-sdk/jsx';
await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: (
<Box>
<Heading>Alert heading</Heading>
<Text>Something happened in the system.</Text>
</Box>
),
},
});
Components
The following custom UI JSX 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
import { Box, Heading, Address } from '@metamask/snaps-sdk/jsx';
await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: (
<Box>
<Heading>Are you sure you want to send tokens to this address?</Heading>
<Address>0x000000000000000000000000000000000000dEaD</Address>
</Box>
),
},
});


Bold
Outputs bold text.
Example
import { Box, Heading, Text, Bold } from '@metamask/snaps-sdk/jsx';
await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: (
<Box>
<Heading>Hello world!</Heading>
<Text>
This is <Bold>bold</Bold>.
</Text>
</Box>
),
},
});
Box
Outputs a box, which can be used as a container for other components. This component takes an array of custom UI JSX components.
Example
import { Bold, Box, Heading, Text } from '@metamask/snaps-sdk/jsx';
module.exports.onTransaction = async ({ transaction }) => {
const gasFeesPercentage = /* Calculate gas fees percentage */;
return {
content: (
<Box>
<Heading>Transaction insights</Heading>
<Text>
As set up, you are paying <Bold>{gasFeesPercentage.toFixed(2)}%</Bold> in gas fees for this transaction.
</Text>
</Box>
),
};
};
Button
Outputs a button that the user can select. For use in interactive UI.
Props
children
:string
- The text of the button.type
:string
- (Optional) Possible values arebutton
orsubmit
. The default isbutton
.name
:string
- (Optional) The name that will be sent toonUserInput
when a user selects the button.variant
- (Optional) Determines the appearance of the button. Possible values areprimary
orsecondary
. The default isprimary
.
Example
import { Box, Heading, Button } from '@metamask/snaps-sdk/jsx';
const interfaceId = await snap.request({
method: "snap_createInterface",
params: {
ui: (
<Box>
<Heading>Interactive interface</Heading>
<Button name="interactive-button">Click me</Button>
</Box>
),
},
});
await snap.request({
method: "snap_dialog",
params: {
type: "Alert",
id: interfaceId,
},
});
Copyable
Outputs a read-only text field with a copy-to-clipboard shortcut.
Example
import { Box, Text, Copyable } from '@metamask/snaps-sdk/jsx';
await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: (
<Box>
<Text>Your address:</Text>
<Copyable>0x000000000000000000000000000000000000dEaD</Copyable>
</Box>
),
},
});
Divider
Outputs a horizontal divider.
Example
import { Box, Heading, Divider, Text } from '@metamask/snaps-sdk/jsx';
module.exports.onHomePage = async () => {
return {
content: (
<Box>
<Heading>Hello world!</Heading>
<Divider />
<Text>Welcome to my Snap home page!</Text>
</Box>
),
};
};
Dropdown
Outputs a dropdown for use in interactive UI.
Props
name
:string
- The name that will be sent toonUserInput
children
:Option[]
- One or moreOption
components with props:value
:string
- The value that will be sent toonUserInput
children
:string
- The text that will be displayed in the dropdown for that option
Example
import { Box, Text, Dropdown } from '@metamask/snaps-sdk/jsx';
const interfaceId = await snap.request({
method: "snap_createInterface",
params: {
ui: (
<Box>
<Text>Pick a currency</Text>
<Dropdown name="currency">
<Option value="ETH">ETH</Option>
<Option value="USD">USD</Option>
</Dropdown>
</Box>
),
},
});
await snap.request({
method: "snap_dialog",
params: {
type: "Alert",
id: interfaceId,
},
});


Form
Outputs a form for use in interactive UI.
Props
name
:string
- The name that will be sent toonUserInput
when a user interacts with the form.children
:array
- An array ofInput
orButton
components.
Example
import { Form, Input, Button } from '@metamask/snaps-sdk/jsx';
const interfaceId = await snap.request({
method: "snap_createInterface",
params: {
ui: (
<Form name="form-to-fill">
<Input name="user-name" placeholder="Your name" />
<Button type="submit">Submit</Button>
</Form>
),
},
});
await snap.request({
method: "snap_dialog",
params: {
type: "Alert",
id: interfaceId,
},
});
Heading
Outputs a heading.
This is useful for Box
titles.
Example
import { Box, Heading, Text } from '@metamask/snaps-sdk/jsx';
module.exports.onHomePage = async () => {
return {
content: (
<Box>
<Heading>Hello world!</Heading>
<Text>Welcome to my Snap home page!</Text>
</Box>
),
};
};
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.
To disable image support, set the features.images
configuration option to false
.
The default is true
.
Props
src
:string
- An inline SVG.alt
:string
- An optional alternative text for the image.
Example
import { Box, Heading, Text, Image } from '@metamask/snaps-sdk/jsx';
import svgIcon from "./path/to/icon.svg";
module.exports.onHomePage = async () => {
return {
content: (
<Box>
<Heading>Hello world!</Heading>
<Text>Welcome to my Snap home page!</Text>
<Image src={svgIcon} />
</Box>
),
};
};
See the @metamask/images-example-snap
package for a full example of implementing images.
Input
Outputs an input component for use in interactive UI.
Props
name
:string
- The name that will be used as a key to the event sent toonUserInput
when the containing form is submitted.type
:string
- (Optional) Type of input. Possible values aretext
,number
, orpassword
. The default istext
.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 { Form, Input, Button } from '@metamask/snaps-sdk/jsx';
const interfaceId = await snap.request({
method: "snap_createInterface",
params: {
ui: (
<Form name="form-to-fill">
<Input name="user-name" placeholder="Your name" />
<Button type="submit">Submit</Button>
</Form>
),
},
});
await snap.request({
method: "snap_dialog",
params: {
type: "Alert",
id: interfaceId,
},
});
Italic
Outputs italic text.
Example
import { Box, Heading, Text, Italic } from '@metamask/snaps-sdk/jsx';
await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: (
<Box>
<Heading>Hello world!</Heading>
<Text>
This is <Italic>italic</Italic>.
</Text>
</Box>
),
},
});
Link
Outputs a clickable link.
Props
href
:string
- The URL to point to.children
: Array<string
|Bold
|Italic
> - The text to be clicked.
Example
import { Box, Heading, Link, Text } from '@metamask/snaps-sdk/jsx';
module.exports.onHomePage = async () => {
return {
content: (
<Box>
<Heading>Hello world!</Heading>
<Text>
Download <Link href="https://metamask.io">MetaMask</Link>.
</Text>
<Text>
Read the MetaMask docs at <Link href="https://docs.metamask.io">MetaMask docs</Link>.
</Text>
</Box>
),
};
};
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
, Image
, or Address
.
Props
label
:string
- The label of the rowvariant
:string
- Optional, the variant of the label. Can be one ofdefault
,error
, orwarning
.children
:Address
|Image
|Text
- The value of the row.
Example
import { Box, Row, Text, Address } from '@metamask/snaps-sdk/jsx';
await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: (
<Box>
<Row label="Address">
<Address>0x000000000000000000000000000000000000dEaD</Address>
</Row>
<Row label="Balance">
<Text>1.78 ETH</Text>
</Row>
</Box>
),
},
});
Spinner
Outputs a loading indicator.
Example
import { Box, Heading, Spinner } from '@metamask/snaps-sdk/jsx';
await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: (
<Box>
<Heading>Please wait...</Heading>
<Spinner />
</Box>
),
},
});
Text
Outputs text.
Example
import { Box, Heading, Text } from '@metamask/snaps-sdk/jsx';
module.exports.onHomePage = async () => {
return {
content: (
<Box>
<Heading>Hello world!</Heading>
<Text>Welcome to my Snap home page!</Text>
</Box>
),
};
};
Emojis
Text-based components (such as Heading
and Text
) accept emojis.
Example
import { Box, Heading, Text } from '@metamask/snaps-sdk/jsx';
await snap.request({
method: "snap_dialog",
params: {
type: "alert",
content: (
<Box>
<Heading>Hello world!</Heading>
<Text>This is an apple 🍎 and this is an orange 🍊.</Text>
</Box>
),
},
});