diff --git a/docs/whats-new.md b/docs/whats-new.md
index 97e92cec550..d14746b29a6 100644
--- a/docs/whats-new.md
+++ b/docs/whats-new.md
@@ -19,6 +19,8 @@ of the [MetaMask developer page](https://metamask.io/developer/).
## June 2024
+- Documented [Snaps custom UI with JSX](/snaps/features/custom-ui/with-jsx).
+ ([#1348](https://github.com/MetaMask/metamask-docs/pull/1348))
- Updated [React dapp with global state tutorial](/wallet/tutorials/react-dapp-global-state) with
instructions for EIP-6963.
([#1330](https://github.com/MetaMask/metamask-docs/pull/1330))
diff --git a/services/index.md b/services/index.md
index 5c6c9f4541a..bacbc387883 100644
--- a/services/index.md
+++ b/services/index.md
@@ -3,9 +3,9 @@ sidebar_label: Introduction
sidebar_position: 1
---
-import CodeTerminal from '@site/src/components/CodeTerminal/CodeTerminal.jsx';
-import SectionAPIs from '@site/src/components/Sections/SectionAPIs.jsx';
-import SectionNetworks from '@site/src/components/Sections/SectionNetworks.jsx';
+import CodeTerminal from "@site/src/components/CodeTerminal/CodeTerminal.jsx";
+import SectionAPIs from "@site/src/components/Sections/SectionAPIs.jsx";
+import SectionNetworks from "@site/src/components/Sections/SectionNetworks.jsx";
# Build and scale your dapp using services
diff --git a/snaps/assets/custom-ui-box.png b/snaps/assets/custom-ui-box.png
new file mode 100644
index 00000000000..11e9b04de19
Binary files /dev/null and b/snaps/assets/custom-ui-box.png differ
diff --git a/snaps/assets/custom-ui-dropdown-active.png b/snaps/assets/custom-ui-dropdown-active.png
new file mode 100644
index 00000000000..6db3a940bc2
Binary files /dev/null and b/snaps/assets/custom-ui-dropdown-active.png differ
diff --git a/snaps/assets/custom-ui-dropdown.png b/snaps/assets/custom-ui-dropdown.png
new file mode 100644
index 00000000000..2f0bb094bf9
Binary files /dev/null and b/snaps/assets/custom-ui-dropdown.png differ
diff --git a/snaps/assets/custom-ui-field.png b/snaps/assets/custom-ui-field.png
new file mode 100644
index 00000000000..0ad7cd9400d
Binary files /dev/null and b/snaps/assets/custom-ui-field.png differ
diff --git a/snaps/features/cron-jobs.md b/snaps/features/cron-jobs.md
index cc87cf056e3..92b4c32bc32 100644
--- a/snaps/features/cron-jobs.md
+++ b/snaps/features/cron-jobs.md
@@ -3,6 +3,9 @@ description: Schedule periodic actions for your users.
sidebar_position: 1
---
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+
# Cron jobs
You can schedule actions to run periodically at fixed times or intervals, also known as "cron jobs."
@@ -40,8 +43,43 @@ Expose an [`onCronjob`](../reference/entry-points.md#oncronjob) entry point, whi
the specified schedule with the requests defined in the `endowment:cronjob` permission.
The following example handles the `execute` method specified in the previous example:
+
+
+
+
+```tsx title="index.tsx"
+import type { OnCronjobHandler } from "@metamask/snaps-sdk";
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
+
+export const onCronjob: OnCronjobHandler = async ({ request }) => {
+ switch (request.method) {
+ case "execute":
+ // Cron jobs can execute any method that is available to the Snap.
+ return snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "alert",
+ content: (
+
+ Cron job
+ This dialog was triggered by a cron job.
+
+ ),
+ },
+ });
+
+ default:
+ throw new Error("Method not found.");
+ }
+};
+```
+
+
+
+
```typescript title="index.ts"
-import type { OnCronjobHandler } from "@metamask/snaps-sdk"
+import type { OnCronjobHandler } from "@metamask/snaps-sdk";
+import { panel, heading, text } from "@metamask/snaps-sdk";
export const onCronjob: OnCronjobHandler = async ({ request }) => {
switch (request.method) {
@@ -64,6 +102,9 @@ export const onCronjob: OnCronjobHandler = async ({ request }) => {
}
```
+
+
+
:::tip Access data from cron jobs
When accessing [encrypted data](data-storage.md#2-use-encrypted-storage) from cron jobs, MetaMask
requires the user to enter their password if the wallet is locked.
diff --git a/snaps/features/custom-ui/dialogs.md b/snaps/features/custom-ui/dialogs.md
index c4aacb4f82d..80831ad3d30 100644
--- a/snaps/features/custom-ui/dialogs.md
+++ b/snaps/features/custom-ui/dialogs.md
@@ -3,6 +3,9 @@ description: Display custom alert, confirmation, or prompt screens in MetaMask.
sidebar_position: 2
---
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+
# Dialogs
You can display a dialog in the MetaMask UI using the
@@ -35,6 +38,31 @@ To display an alert that can only be acknowledged, call
[`snap_dialog`](../../reference/snaps-api.md#snap_dialog) with `type: "alert"`.
The following example displays custom UI that alerts the user when something happens in the system:
+
+
+
+```tsx title="index.tsx"
+import { Box, Text, Heading } from "@metamask/snaps-sdk/jsx";
+
+await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "alert",
+ content: (
+
+ Something happened in the system
+ The thing that happened is...
+
+ ),
+ },
+});
+
+// Code that should execute after the alert has been acknowledged.
+```
+
+
+
+
```javascript title="index.js"
import { panel, text, heading } from "@metamask/snaps-sdk"
@@ -52,6 +80,10 @@ await snap.request({
// Code that should execute after the alert has been acknowledged.
```
+
+
+
+
@@ -63,6 +95,33 @@ To display a confirmation that can be accepted or rejected, call
The following example displays custom UI that asks the user to confirm whether they would like to
take an action:
+
+
+
+```tsx title="index.tsx"
+import { Box, Text, Heading } from "@metamask/snaps-sdk/jsx";
+
+const result = await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "confirmation",
+ content: (
+
+ Would you like to take the action?
+ The action is...
+
+ ),
+ },
+});
+
+if (result === true) {
+ // Do the action.
+}
+```
+
+
+
+
```javascript title="index.js"
import { panel, text, heading } from "@metamask/snaps-sdk"
@@ -82,6 +141,9 @@ if (result === true) {
}
```
+
+
+
@@ -94,6 +156,32 @@ Prompt dialogs also accept a `placeholder` value that displays in the input fiel
The following example displays custom UI that prompts the user to enter a wallet address:
+
+
+
+```tsx title="index.tsx"
+import { Box, Text, Heading } from "@metamask/snaps-sdk/jsx";
+
+const walletAddress = await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "prompt",
+ content: (
+
+ What is the wallet address?
+ Please enter the wallet address to be monitored
+
+ ),
+ placeholder: "0x123...",
+ },
+});
+
+// walletAddress will be a string containing the address entered by the user.
+```
+
+
+
+
```javascript title="index.js"
import { panel, text, heading } from "@metamask/snaps-sdk"
@@ -112,6 +200,9 @@ const walletAddress = await snap.request({
// walletAddress will be a string containing the address entered by the user.
```
+
+
+
diff --git a/snaps/features/custom-ui/home-pages.md b/snaps/features/custom-ui/home-pages.md
index b7fcfc34e44..7fd7fe0285f 100644
--- a/snaps/features/custom-ui/home-pages.md
+++ b/snaps/features/custom-ui/home-pages.md
@@ -3,6 +3,9 @@ description: Display a dedicated UI page in MetaMask for your Snap.
sidebar_position: 3
---
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+
# Home pages
You can display a dedicated UI, or "home page," for your Snap within MetaMask.
@@ -32,6 +35,28 @@ MetaMask calls this method when a user selects your Snap name in the Snaps menu.
The following example displays custom UI that welcomes the user to the Snap's home page:
+
+
+
+```tsx title="index.tsx"
+import type { OnHomePageHandler } from "@metamask/snaps-sdk";
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
+
+export const onHomePage: OnHomePageHandler = async () => {
+ return {
+ content: (
+
+ Hello world!
+ Welcome to my Snap home page!
+
+ ),
+ };
+};
+```
+
+
+
+
```typescript title="index.ts"
import type { OnHomePageHandler } from "@metamask/snaps-sdk"
import { panel, text, heading } from "@metamask/snaps-sdk"
@@ -46,6 +71,9 @@ export const onHomePage: OnHomePageHandler = async () => {
}
```
+
+
+
diff --git a/snaps/features/custom-ui/index.md b/snaps/features/custom-ui/index.md
index e886f0fe169..517e9c23f4f 100644
--- a/snaps/features/custom-ui/index.md
+++ b/snaps/features/custom-ui/index.md
@@ -5,13 +5,18 @@ sidebar_position: 4
# Custom UI
+:::caution
+This version of custom UI is deprecated. If you're building a new Snaps project,
+use [custom UI with JSX](./with-jsx). JSX is supported in MetaMask Extension and Flask version 12+.
+:::
+
You can display custom user interface (UI) components using the
[`@metamask/snaps-sdk`](https://github.com/MetaMask/snaps/tree/main/packages/snaps-sdk) module when
implementing the following features:
- [Dialogs](dialogs.md)
- [Home pages](home-pages.md)
-- [Transaction insights](../../reference/entry-points.md#ontransaction)
+- [Transaction insights](../transaction-insights.md)
- [Signature insights](../signature-insights.md)
To use custom UI, first install [`@metamask/snaps-sdk`](https://github.com/MetaMask/snaps/tree/main/packages/snaps-sdk)
@@ -309,7 +314,7 @@ An object containing:
#### Example
```js
-import { input, form } from "@metamask/snaps-sdk"
+import { button, input, form } from "@metamask/snaps-sdk";
const interfaceId = await snap.request({
method: "snap_createInterface",
diff --git a/snaps/features/custom-ui/interactive-ui.md b/snaps/features/custom-ui/interactive-ui.md
index d3ff0a7a81f..7eb54e057d3 100644
--- a/snaps/features/custom-ui/interactive-ui.md
+++ b/snaps/features/custom-ui/interactive-ui.md
@@ -16,6 +16,14 @@ The following interactive UI components are available:
- [`form`](index.md#form)
- [`input`](index.md#input)
+The following interactive UI JSX components are available:
+
+- [`Button`](with-jsx.md#button)
+- [`Dropdown`](with-jsx.md#dropdown)
+- [`Field`](with-jsx.md#field)
+- [`Form`](with-jsx.md#form)
+- [`Input`](with-jsx.md#input)
+
## Create an interactive interface
Create an interactive interface using the
diff --git a/snaps/features/custom-ui/with-jsx.md b/snaps/features/custom-ui/with-jsx.md
new file mode 100644
index 00000000000..efc28a828e6
--- /dev/null
+++ b/snaps/features/custom-ui/with-jsx.md
@@ -0,0 +1,687 @@
+---
+description: Display custom user interface components using JSX.
+sidebar_position: 4
+---
+
+# Custom UI with JSX
+
+You can display custom user interface (UI) JSX components using the
+[`@metamask/snaps-sdk`](https://github.com/MetaMask/snaps/tree/main/packages/snaps-sdk) module when
+implementing the following features:
+
+- [Dialogs](dialogs.md)
+- [Home pages](home-pages.md)
+- [Transaction insights](../transaction-insights.md)
+- [Signature insights](../signature-insights.md)
+
+:::note
+JSX is supported in MetaMask Extension and Flask version 12+. New UI components will be added as JSX components. The previous function-based library is deprecated.
+:::
+
+To use custom UI with JSX, first install [`@metamask/snaps-sdk`](https://github.com/MetaMask/snaps/tree/main/packages/snaps-sdk)
+using the following command:
+
+```bash
+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`](#box) (the [`panel`](./index.md#panel) function equivalent) using [`snap_dialog`](../../reference/snaps-api.md#snap_dialog):
+
+```javascript title="index.jsx"
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
+
+await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "alert",
+ content: (
+
+ Alert heading
+ Something happened in the system.
+
+ ),
+ },
+});
+```
+
+:::note
+Note that JSX can only be used in `.jsx` or `.tsx` files.
+:::
+
+## 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
+
+```javascript title="index.jsx"
+import { Box, Heading, Address } from "@metamask/snaps-sdk/jsx";
+
+await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "alert",
+ content: (
+
+ Are you sure you want to send tokens to this address?
+ 0x000000000000000000000000000000000000dEaD
+
+ ),
+ },
+});
+```
+
+
+
+
+
+
+
+
+
+
+### `Bold`
+
+Outputs bold text.
+
+#### Example
+
+```javascript title="index.jsx"
+import { Box, Heading, Text, Bold } from "@metamask/snaps-sdk/jsx";
+
+await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "alert",
+ content: (
+
+ Hello world!
+
+ This is bold.
+
+
+ ),
+ },
+});
+```
+
+### `Box`
+
+Outputs a box, which can be used as a container for other components.
+
+#### Props
+
+- `direction` - (Optional) The direction in which elements flow inside the box.
+ Possible values are `"horizontal"` or `"vertical"`.
+ The default is `"vertical"`.
+- `alignment` - (Optional) The alignment of the elements inside the box.
+ Possible values are `"start"`, `"center"`, `"end"`, `"space-between"`, or `"space-around"`.
+ The default is `"start"`.
+
+#### Example
+
+```javascript title="index.jsx"
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
+
+module.exports.onHomePage = async () => {
+ return {
+ content: (
+
+ Features
+
+ Feature 1
+ Feature 2
+ Feature 3
+
+
+ ),
+ };
+};
+```
+
+
+
+
+
+### `Button`
+
+Outputs a button that the user can select.
+For use in [interactive UI](interactive-ui.md).
+
+#### Props
+
+- `children`: `string` - The text of the button.
+- `type` - (Optional) The type of button.
+ Possible values are `"button"` or `"submit"`.
+ The default is `"button"`.
+- `name`: `string` - (Optional) The name that will be sent to [`onUserInput`](../../reference/entry-points.md#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
+
+```javascript
+import { Box, Heading, Button } from "@metamask/snaps-sdk/jsx";
+
+const interfaceId = await snap.request({
+ method: "snap_createInterface",
+ params: {
+ ui: (
+
+ Interactive interface
+
+
+ ),
+ },
+});
+
+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
+
+```javascript title="index.jsx"
+import { Box, Text, Copyable } from "@metamask/snaps-sdk/jsx";
+
+await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "alert",
+ content: (
+
+ Your address:
+ 0x000000000000000000000000000000000000dEaD
+
+ ),
+ },
+});
+```
+
+
+
+
+
+### `Divider`
+
+Outputs a horizontal divider.
+
+#### Example
+
+```javascript title="index.jsx"
+import { Box, Heading, Divider, Text } from "@metamask/snaps-sdk/jsx";
+
+module.exports.onHomePage = async () => {
+ return {
+ content: (
+
+ Hello world!
+
+ Welcome to my Snap home page!
+
+ ),
+ };
+};
+```
+
+
+
+
+
+### `Dropdown`
+
+Outputs a dropdown for use in [interactive UI](interactive-ui.md).
+
+#### Props
+
+- `name`: `string` - The name sent to [`onUserInput`](../../reference/entry-points.md#onuserinput).
+- `children`: `Option[]` - One or more `Option` components with the following props:
+ - `value`: `string` - The value sent to [`onUserInput`](../../reference/entry-points.md#onuserinput).
+ - `children`: `string` - The text displayed in the dropdown for that option.
+
+#### Example
+
+```js
+import { Box, Text, Dropdown } from "@metamask/snaps-sdk/jsx";
+
+const interfaceId = await snap.request({
+ method: "snap_createInterface",
+ params: {
+ ui: (
+
+ Pick a currency
+
+
+
+
+
+ ),
+ },
+});
+
+await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "Alert",
+ id: interfaceId,
+ },
+});
+```
+
+
+
+
+
+
+
+
+
+
+### `Field`
+
+Outputs a form field, wrapping a [`Dropdown`](#dropdown) or [`Input`](#input) to give it a label and optional error.
+
+#### Props
+
+- `label`: `string` - The label for the wrapped element.
+- `error`: `string` - Any error for the wrapped element. Setting this changes the styling of the wrapped element to show that there is an error.
+- `children` - The [`Dropdown`](#dropdown) or [`Input`](#input) element to be wrapped.
+
+#### Example
+
+```js
+import { Field, Form, Input, Button } from "@metamask/snaps-sdk/jsx";
+
+const interfaceId = await snap.request({
+ method: "snap_createInterface",
+ params: {
+ ui: (
+
+ ),
+ },
+});
+
+await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "Alert",
+ id: interfaceId,
+ },
+});
+```
+
+
+
+
+
+### `Form`
+
+Outputs a form for use in [interactive UI](interactive-ui.md).
+
+#### Props
+
+- `name`: `string` - The name that will be sent to [`onUserInput`](../../reference/entry-points.md#onuserinput)
+ when a user interacts with the form.
+- `children`: `array` - An array of [`Input`](#input) or [`Button`](#button) components.
+
+#### Example
+
+```js
+import { Form, Input, Button } from "@metamask/snaps-sdk/jsx";
+
+const interfaceId = await snap.request({
+ method: "snap_createInterface",
+ params: {
+ ui: (
+
+ ),
+ },
+});
+
+await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "Alert",
+ id: interfaceId,
+ },
+});
+```
+
+
+
+
+
+### `Heading`
+
+Outputs a heading.
+This is useful for [`Box`](#box) titles.
+
+#### Example
+
+```javascript title="index.jsx"
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
+
+module.exports.onHomePage = async () => {
+ return {
+ content: (
+
+ Hello world!
+ Welcome to my Snap home page!
+
+ ),
+ };
+};
+```
+
+
+
+
+
+### `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 `` tag, which prevents JavaScript or interaction events from
+being supported.
+
+:::note
+To disable image support, set the [`features.images`](../../reference/cli/options.md#featuresimages)
+configuration option to `false`.
+The default is `true`.
+:::
+
+#### Props
+
+- `src`: `string` - An inline SVG.
+- `alt`: `string` - An optional alternative text for the image.
+
+#### Example
+
+```javascript title="index.jsx"
+import { Box, Heading, Text, Image } from "@metamask/snaps-sdk/jsx";
+import svgIcon from "./path/to/icon.svg";
+
+module.exports.onHomePage = async () => {
+ return {
+ content: (
+
+ Hello world!
+ Welcome to my Snap home page!
+
+
+ ),
+ };
+};
+```
+
+
+
+
+
+:::note
+See the [`@metamask/images-example-snap`](https://github.com/MetaMask/snaps/tree/main/packages/examples/packages/images)
+package for a full example of implementing images.
+:::
+
+### `Input`
+
+Outputs an input component for use in [interactive UI](interactive-ui.md).
+
+#### Props
+
+- `name`: `string` - The name that will be used as a key to the event sent to
+ [`onUserInput`](../../reference/entry-points.md#onuserinput) when the containing form is submitted.
+- `type` - (Optional) The 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
+
+```js
+import { Form, Input, Button } from "@metamask/snaps-sdk/jsx";
+
+const interfaceId = await snap.request({
+ method: "snap_createInterface",
+ params: {
+ ui: (
+
+ ),
+ },
+});
+
+await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "Alert",
+ id: interfaceId,
+ },
+});
+```
+
+
+
+
+
+### `Italic`
+
+Outputs italic text.
+
+#### Example
+
+```javascript title="index.jsx"
+import { Box, Heading, Text, Italic } from "@metamask/snaps-sdk/jsx";
+
+await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "alert",
+ content: (
+
+ Hello world!
+
+ This is italic.
+
+
+ ),
+ },
+});
+```
+
+### `Link`
+
+Outputs a clickable link.
+
+#### Props
+
+- `href`: `string` - The URL to point to.
+- `children`: `Array` - The link text.
+
+#### Example
+
+```javascript title="index.jsx"
+import { Box, Heading, Link, Text } from "@metamask/snaps-sdk/jsx";
+
+module.exports.onHomePage = async () => {
+ return {
+ content: (
+
+ Hello world!
+
+ Download MetaMask.
+
+
+ Read the MetaMask docs at MetaMask docs.
+
+
+ ),
+ };
+};
+```
+
+
+
+
+
+### `Row`
+
+Outputs a row with a label and value, which can be used for key-value data.
+
+#### Props
+
+- `label`: `string` - The label of the row.
+- `variant` - (Optional) The variant of the label.
+ Possible values are `"default"`, `"error"`, or `"warning"`.
+- `children` - The value of the row, which can be a [`Text`](#text), [`Image`](#image), or
+ [`Address`](#address) component.
+
+#### Example
+
+```javascript title="index.jsx"
+import { Box, Row, Text, Address } from "@metamask/snaps-sdk/jsx";
+
+await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "alert",
+ content: (
+
+
+ 0x000000000000000000000000000000000000dEaD
+
+
+ 1.78 ETH
+
+
+ ),
+ },
+});
+```
+
+
+
+### `Text`
+
+Outputs text.
+
+#### Example
+
+```javascript title="index.jsx"
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
+
+module.exports.onHomePage = async () => {
+ return {
+ content: (
+
+ Hello world!
+ Welcome to my Snap home page!
+
+ ),
+ };
+};
+```
+
+
+
+
+
+## Emojis
+
+Text-based components (such as [`Heading`](#heading) and [`Text`](#text)) accept emojis.
+
+#### Example
+
+```javascript title="index.jsx"
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
+
+await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "alert",
+ content: (
+
+ Hello world!
+ This is an apple 🍎 and this is an orange 🍊.
+
+ ),
+ },
+});
+```
+
+
+
+
\ No newline at end of file
diff --git a/snaps/features/lifecycle-hooks.md b/snaps/features/lifecycle-hooks.md
index ce7db582ddd..f748b857f87 100644
--- a/snaps/features/lifecycle-hooks.md
+++ b/snaps/features/lifecycle-hooks.md
@@ -3,6 +3,9 @@ sidebar_position: 6
description: Call an action when your Snap is installed or updated.
---
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+
# Lifecycle hooks
You can implement lifecycle hooks to automatically run an action, such as displaying a dialog or
@@ -30,6 +33,35 @@ For example, you can use `onInstall` to perform any initialization that is requi
The following example displays an [alert dialog](custom-ui/dialogs.md#display-an-alert-dialog) upon installation:
+
+
+
+
+```tsx title="index.tsx"
+import type { OnInstallHandler } from "@metamask/snaps-sdk";
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
+
+export const onInstall: OnInstallHandler = async () => {
+ await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "alert",
+ content: (
+
+ Installation successful
+
+ To use this Snap, visit the companion dapp at metamask.io.
+
+
+ ),
+ },
+ });
+};
+```
+
+
+
+
```typescript title="index.ts"
import type { OnInstallHandler } from "@metamask/snaps-sdk"
import { heading, panel, text } from "@metamask/snaps-sdk"
@@ -50,6 +82,9 @@ export const onInstall: OnInstallHandler = async () => {
}
```
+
+
+
### 3. Run an action on update
To run an action when a user updates your Snap, expose the
@@ -58,6 +93,34 @@ For example, you can use `onUpdate` to perform any migrations that are required
The following example displays an [alert dialog](custom-ui/dialogs.md#display-an-alert-dialog) upon update:
+
+
+
+
+```tsx title="index.tsx"
+import type { OnUpdateHandler } from "@metamask/snaps-sdk";
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
+
+export const onUpdate: OnUpdateHandler = async () => {
+ await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "alert",
+ content: (
+
+ Update successful
+ New features added in this version:
+ Added a dialog that appears when updating.
+
+ ),
+ },
+ });
+};
+```
+
+
+
+
```typescript title="index.ts"
import type { OnUpdateHandler } from "@metamask/snaps-sdk"
import { heading, panel, text } from "@metamask/snaps-sdk"
@@ -77,6 +140,9 @@ export const onUpdate: OnUpdateHandler = async () => {
}
```
+
+
+
## Example
See the [`@metamask/lifecycle-hooks-example-snap`](https://github.com/MetaMask/snaps/tree/main/packages/examples/packages/lifecycle-hooks)
diff --git a/snaps/features/signature-insights.md b/snaps/features/signature-insights.md
index dc06781a54b..4c70c39d575 100644
--- a/snaps/features/signature-insights.md
+++ b/snaps/features/signature-insights.md
@@ -124,6 +124,36 @@ level of `SeverityLevel.Critical`.
The following is an example implementation of `onSignature`:
+
+
+
+```tsx title="index.tsx"
+import type { OnSignatureHandler, SeverityLevel } from "@metamask/snaps-sdk";
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
+
+export const onSignature: OnSignatureHandler = async ({
+ signature,
+ signatureOrigin,
+}) => {
+ const insights = /* Get insights based on custom logic */;
+ return {
+ content: (
+
+ My Signature Insights
+ Here are the insights:
+ {insights.map((insight) => (
+ {insight.value}
+ ))}
+
+ ),
+ severity: SeverityLevel.Critical,
+ };
+};
+```
+
+
+
+
```typescript title="index.ts"
import type { OnSignatureHandler, SeverityLevel } from "@metamask/snaps-sdk";
import { panel, heading, text } from "@metamask/snaps-sdk";
@@ -144,6 +174,9 @@ export const onSignature: OnSignatureHandler = async ({
};
```
+
+
+
When your Snap returns a signature insight with a `severity` of `SeverityLevel.Critical`, the custom
UI displays in a modal after the user selects the **Sign** button.
For example:
diff --git a/snaps/features/transaction-insights.md b/snaps/features/transaction-insights.md
index 716ea9df48c..99f8ffd28bb 100644
--- a/snaps/features/transaction-insights.md
+++ b/snaps/features/transaction-insights.md
@@ -4,6 +4,9 @@ toc_max_heading_level: 4
sidebar_position: 13
---
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+
# Transaction insights
You can provide transaction insights in MetaMask's transaction confirmation window before a user
@@ -43,6 +46,36 @@ handler method.
The following is an example implementation of `onTransaction`:
+
+
+
+```tsx title="index.tsx"
+import type { OnTransactionHandler } from "@metamask/snaps-sdk";
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
+
+export const onTransaction: OnTransactionHandler = async ({
+ transaction,
+ chainId,
+ transactionOrigin,
+}) => {
+ const insights = /* Get insights */;
+ return {
+ content: (
+
+ My Transaction Insights
+ Here are the insights:
+ {insights.map((insight) => (
+ {insight.value}
+ ))}
+
+ ),
+ };
+};
+```
+
+
+
+
```typescript title="index.ts"
import type { OnTransactionHandler } from "@metamask/snaps-sdk";
import { panel, heading, text } from "@metamask/snaps-sdk";
@@ -63,6 +96,10 @@ export const onTransaction: OnTransactionHandler = async ({
};
```
+
+
+
+
The Snap tab in the transaction confirmation window displays the transaction insights:
@@ -77,6 +114,38 @@ The Snap tab in the transaction confirmation window displays the transaction ins
A Snap providing transaction insights can return an optional severity level of `"critical"`.
MetaMask shows a modal with the warning before the user can confirm the transaction.
+
+
+
+```tsx title="index.tsx"
+import type { OnTransactionHandler } from "@metamask/snaps-sdk";
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
+
+export const onTransaction: OnTransactionHandler = async ({
+ transaction,
+ chainId,
+ transactionOrigin,
+}) => {
+ const insights = /* Get insights */;
+ return {
+ content: (
+
+ My Transaction Insights
+ Here are the insights:
+ {insights.map((insight) => (
+ {insight.value}
+ ))}
+
+ ),
+ // highlight-next-line
+ severity: "critical",
+ };
+};
+```
+
+
+
+
```typescript title="index.ts"
import type { OnTransactionHandler } from "@metamask/snaps-sdk";
import { panel, heading, text } from "@metamask/snaps-sdk";
@@ -99,6 +168,9 @@ export const onTransaction: OnTransactionHandler = async ({
};
```
+
+
+
diff --git a/snaps/get-started/quickstart.md b/snaps/get-started/quickstart.md
index b58e51c8250..10fefbbec4d 100644
--- a/snaps/get-started/quickstart.md
+++ b/snaps/get-started/quickstart.md
@@ -3,7 +3,9 @@ description: Get started quickly using the create-snap starter kit.
sidebar_position: 2
---
-import YoutubeEmbed from '@site/src/components/YoutubeEmbed';
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+import YoutubeEmbed from "@site/src/components/YoutubeEmbed";
# Snaps quickstart
@@ -89,6 +91,50 @@ You can customize your Snap by editing `index.ts` in the `packages/snap/src` fol
`index.ts` contains an example request that uses the
[`snap_dialog`](../reference/snaps-api.md#snapdialog) method to display a custom confirmation screen:
+
+
+
+```tsx title="index.tsx"
+import type { OnRpcRequestHandler } from "@metamask/snaps-sdk";
+import { Box, Text, Bold } from "@metamask/snaps-sdk/jsx";
+
+/**
+ * Handle incoming JSON-RPC requests, sent through wallet_invokeSnap.
+ *
+ * @param args - The request handler arguments as an 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 ({
+ origin,
+ request,
+}) => {
+ switch (request.method) {
+ case "hello":
+ return snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "confirmation",
+ content: (
+
+ Hello, {origin}!
+ This custom confirmation is just for display purposes.
+ But you can edit the Snap source code to make it do something, if you want to!
+
+ ),
+ },
+ });
+ default:
+ throw new Error("Method not found.");
+ }
+};
+```
+
+
+
+
```ts title="index.ts"
import type { OnRpcRequestHandler } from "@metamask/snaps-sdk"
import { panel, text } from "@metamask/snaps-sdk"
@@ -127,6 +173,9 @@ export const onRpcRequest: OnRpcRequestHandler = async ({
}
```
+
+
+
Edit the text in any `text()` component and select the **Reconnect** button
on the front-end to re-install the Snap.
diff --git a/snaps/how-to/use-environment-variables.md b/snaps/how-to/use-environment-variables.md
index 67ac057db4f..16ba0abec71 100644
--- a/snaps/how-to/use-environment-variables.md
+++ b/snaps/how-to/use-environment-variables.md
@@ -91,19 +91,47 @@ Snaps CLI:
3. You can also use environment variables directly in your Snap.
For example:
- ```typescript title="index.ts"
- import { panel, text, heading } from "@metamask/snaps-sdk"
-
- await snap.request({
- method: "snap_dialog",
- params: {
- type: "alert",
- content: panel([
- heading("This custom alert is just for display purposes."),
- text(
- `SNAP_ENV is ${process.env.SNAP_ENV}, PUBLIC_KEY is ${process.env.PUBLIC_KEY}`
- ),
- ]),
- },
- })
- ```
+
+
+
+ ```tsx title="index.tsx"
+ import { Box, Text, Heading } from "@metamask/snaps-sdk/jsx";
+
+ await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "alert",
+ content: (
+
+ This custom alert is just for display purposes.
+
+ SNAP_ENV is {process.env.SNAP_ENV}, PUBLIC_KEY is {process.env.PUBLIC_KEY}
+
+
+ ),
+ },
+ });
+ ```
+
+
+
+
+ ```typescript title="index.ts"
+ import { panel, text, heading } from "@metamask/snaps-sdk"
+
+ await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "alert",
+ content: panel([
+ heading("This custom alert is just for display purposes."),
+ text(
+ `SNAP_ENV is ${process.env.SNAP_ENV}, PUBLIC_KEY is ${process.env.PUBLIC_KEY}`
+ ),
+ ]),
+ },
+ })
+ ```
+
+
+
diff --git a/snaps/learn/tutorials/gas-estimation.md b/snaps/learn/tutorials/gas-estimation.md
index 5ac32b8bf92..da8a3ba1f6e 100644
--- a/snaps/learn/tutorials/gas-estimation.md
+++ b/snaps/learn/tutorials/gas-estimation.md
@@ -3,6 +3,9 @@ description: Create a Snap that estimates gas fees.
sidebar_position: 1
---
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+
# Create a Snap to estimate gas fees
This tutorial walks you through creating a Snap that estimates gas fees.
@@ -175,26 +178,51 @@ only one method, `hello`.
For the `hello` method, the handler returns a call to MetaMask with the parameters to display a
dialog, and passes some static strings.
-Since `getFees()` returns a promise, you must use `then()` to resolve it in your `hello` method.
Update the `hello` method with the following code:
+
+
+
+```tsx title="index.tsx"
+case "hello":
+ const fees = await getFees();
+ return snap.request({
+ method: 'snap_dialog',
+ params: {
+ type: "alert",
+ content: (
+
+ Hello, {origin}!
+ Current gas fee estimates:
+ {fees}
+
+ ),
+ }
+ });
+```
+
+
+
+
```typescript title="index.ts"
case "hello":
- return getFees().then(fees => {
- return snap.request({
- method: 'snap_dialog',
- params: {
- type: "alert",
- content: panel([
- text(`Hello, **${origin}**!`),
- text("Current gas fee estimates:"),
- copyable(fees),
- ]),
- }
- });
+ const fees = await getFees();
+ return snap.request({
+ method: 'snap_dialog',
+ params: {
+ type: "alert",
+ content: panel([
+ text(`Hello, **${origin}**!`),
+ text("Current gas fee estimates:"),
+ copyable(fees),
+ ]),
+ }
});
```
+
+
+
### 5. Build and test the Snap
To build and test your Snap:
diff --git a/snaps/learn/tutorials/transaction-insights.md b/snaps/learn/tutorials/transaction-insights.md
index d5452462667..f39241876f6 100644
--- a/snaps/learn/tutorials/transaction-insights.md
+++ b/snaps/learn/tutorials/transaction-insights.md
@@ -3,6 +3,9 @@ description: Create a Snap that provides transaction insights.
sidebar_position: 2
---
+import Tabs from "@theme/Tabs";
+import TabItem from "@theme/TabItem";
+
# Create a Snap to calculate gas fee percentages
This tutorial walks you through creating a Snap that calculates the percentage of gas fees that
@@ -100,6 +103,58 @@ You do not need any permissions other than `endowment:transaction-insight` and `
To calculate and display the gas fees a user would pay as a percentage of their outgoing transaction,
replace the code in `packages/snap/src/index.ts` with the following:
+
+
+
+```tsx title="index.tsx"
+import type { OnTransactionHandler } from "@metamask/snaps-sdk";
+import { Box, Heading, Text, Bold } from "@metamask/snaps-sdk/jsx";
+
+// Handle outgoing transactions.
+export const onTransaction: OnTransactionHandler = async ({ transaction }) => {
+
+ // Use the Ethereum provider to fetch the gas price.
+ const currentGasPrice = await ethereum.request({
+ method: "eth_gasPrice",
+ }) as string;
+
+ // Get fields from the transaction object.
+ const transactionGas = parseInt(transaction.gas as string, 16);
+ const currentGasPriceInWei = parseInt(currentGasPrice ?? "", 16);
+ const maxFeePerGasInWei = parseInt(transaction.maxFeePerGas as string, 16);
+ const maxPriorityFeePerGasInWei = parseInt(
+ transaction.maxPriorityFeePerGas as string,
+ 16,
+ );
+
+ // Calculate gas fees the user would pay.
+ const gasFees = Math.min(
+ maxFeePerGasInWei * transactionGas,
+ (currentGasPriceInWei + maxPriorityFeePerGasInWei) * transactionGas,
+ );
+
+ // Calculate gas fees as percentage of transaction.
+ const transactionValueInWei = parseInt(transaction.value as string, 16);
+ const gasFeesPercentage = (gasFees / (gasFees + transactionValueInWei)) * 100;
+
+ // Display percentage of gas fees in the transaction insights UI.
+ return {
+ content: (
+
+ Transaction insights Snap
+
+ As set up, you are paying {gasFeesPercentage.toFixed(2)}%
+ in gas fees for this transaction.
+
+
+ ),
+ };
+};
+```
+
+
+
+
```typescript title="index.ts"
import type { OnTransactionHandler } from "@metamask/snaps-sdk"
import { heading, panel, text } from "@metamask/snaps-sdk"
@@ -143,6 +198,9 @@ export const onTransaction: OnTransactionHandler = async ({ transaction }) => {
}
```
+
+
+
:::tip
If you have previously developed a dapp, you're likely familiar with accessing the Ethereum provider using `window.ethereum`.
In a Snap, the `window` object is not available.
@@ -189,6 +247,27 @@ To build and test your Snap:
The Snap should display a gas fee percentage for ETH transfers initiated by the user.
For contract interactions, add the following code to the beginning of the `onTransaction` entry point:
+
+
+
+```tsx title="index.tsx"
+if (typeof transaction.data === "string" && transaction.data !== "0x") {
+ return {
+ content: (
+
+ Percent Snap
+
+ This Snap only provides transaction insights for simple ETH transfers.
+
+
+ ),
+ };
+}
+```
+
+
+
+
```typescript title="index.ts"
if (typeof transaction.data === "string" && transaction.data !== "0x") {
return {
@@ -202,6 +281,9 @@ if (typeof transaction.data === "string" && transaction.data !== "0x") {
}
```
+
+
+
### 6. Next steps
The initial project has generic names in multiple places.
diff --git a/snaps/reference/entry-points.md b/snaps/reference/entry-points.md
index cd25bf0edb8..2b6765a7bd2 100644
--- a/snaps/reference/entry-points.md
+++ b/snaps/reference/entry-points.md
@@ -100,24 +100,26 @@ One of the following:
#### Example
-
+
-```typescript title="index.ts"
-import type { OnHomePageHandler } from "@metamask/snaps-sdk"
-import { panel, text, heading } from "@metamask/snaps-sdk"
+```tsx title="index.tsx"
+import type { OnHomePageHandler } from "@metamask/snaps-sdk";
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
export const onHomePage: OnHomePageHandler = async () => {
return {
- content: panel([
- heading("Hello world!"),
- text("Welcome to my Snap home page!"),
- ]),
- }
-}
+ content: (
+
+ Hello world!
+ Welcome to my Snap home page!
+
+ ),
+ };
+};
```
-
+
```js title="index.js"
import { panel, text, heading } from "@metamask/snaps-sdk"
@@ -153,30 +155,32 @@ None.
#### Example
-
+
-```typescript title="index.ts"
-import type { OnInstallHandler } from "@metamask/snaps-sdk"
-import { heading, panel, text } from "@metamask/snaps-sdk"
+```tsx title="index.tsx"
+import type { OnInstallHandler } from "@metamask/snaps-sdk";
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
export const onInstall: OnInstallHandler = async () => {
await snap.request({
method: "snap_dialog",
params: {
type: "alert",
- content: panel([
- heading("Thank you for installing my Snap"),
- text(
- "To use this Snap, visit the companion dapp at [metamask.io](https://metamask.io)."
- ),
- ]),
+ content: (
+
+ Thank you for installing my Snap
+
+ To use this Snap, visit the companion dapp at metamask.io.
+
+
+ ),
},
- })
-}
+ });
+};
```
-
+
```js title="index.js"
import { heading, panel, text } from "@metamask/snaps-sdk"
@@ -200,6 +204,7 @@ module.exports.onInstall = async () => {
+
## `onKeyringRequest`
To implement the [Account Management API](keyring-api/account-management/index.md) to integrate
@@ -428,11 +433,11 @@ An object containing:
#### Example
-
+
-```typescript title="index.ts"
+```tsx title="index.tsx"
import type { OnSignatureHandler, SeverityLevel } from "@metamask/snaps-sdk";
-import { panel, heading, text } from "@metamask/snaps-sdk";
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
export const onSignature: OnSignatureHandler = async ({
signature,
@@ -440,24 +445,28 @@ export const onSignature: OnSignatureHandler = async ({
}) => {
const insights = /* Get insights */;
return {
- content: panel([
- heading("My Signature Insights"),
- text("Here are the insights:"),
- ...(insights.map((insight) => text(insight.value))),
- ]),
+ content: (
+
+ My Signature Insights
+ Here are the insights:
+ {insights.map((insight) => (
+ {insight.value}
+ ))}
+
+ ),
severity: SeverityLevel.Critical,
};
};
```
-
+
-```js title="index.js"
-import { SeverityLevel } from "@metamask/snaps-sdk";
+```typescript title="index.ts"
+import type { OnSignatureHandler, SeverityLevel } from "@metamask/snaps-sdk";
import { panel, heading, text } from "@metamask/snaps-sdk";
-module.exports.onSignature = async ({
+export const onSignature: OnSignatureHandler = async ({
signature,
signatureOrigin,
}) => {
@@ -476,6 +485,7 @@ module.exports.onSignature = async ({
+
## `onTransaction`
To provide [transaction insights](../features/transaction-insights.md) before a user signs a
@@ -512,11 +522,11 @@ An object containing:
#### Example
-
+
-```typescript title="index.ts"
+```tsx title="index.tsx"
import type { OnTransactionHandler } from "@metamask/snaps-sdk";
-import { panel, heading, text } from "@metamask/snaps-sdk";
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
export const onTransaction: OnTransactionHandler = async ({
transaction,
@@ -525,22 +535,27 @@ export const onTransaction: OnTransactionHandler = async ({
}) => {
const insights = /* Get insights */;
return {
- content: panel([
- heading("My Transaction Insights"),
- text("Here are the insights:"),
- ...(insights.map((insight) => text(insight.value))),
- ]),
+ content: (
+
+ My Transaction Insights
+ Here are the insights:
+ {insights.map((insight) => (
+ {insight.value}
+ ))}
+
+ ),
};
};
```
-
+
-```js title="index.js"
+```typescript title="index.ts"
+import type { OnTransactionHandler } from "@metamask/snaps-sdk";
import { panel, heading, text } from "@metamask/snaps-sdk";
-module.exports.onTransaction = async ({
+export const onTransaction: OnTransactionHandler = async ({
transaction,
chainId,
transactionOrigin,
@@ -577,34 +592,37 @@ None.
#### Example
-
+
-```typescript title="index.ts"
-import type { OnUpdateHandler } from "@metamask/snaps-sdk"
-import { heading, panel, text } from "@metamask/snaps-sdk"
+```tsx title="index.tsx"
+import type { OnUpdateHandler } from "@metamask/snaps-sdk";
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
export const onUpdate: OnUpdateHandler = async () => {
await snap.request({
method: "snap_dialog",
params: {
type: "alert",
- content: panel([
- heading("Thank you for updating my Snap"),
- text("New features added in this version:"),
- text("Added a dialog that appears when updating."),
- ]),
+ content: (
+
+ Thank you for updating my Snap
+ New features added in this version:
+ Added a dialog that appears when updating.
+
+ ),
},
})
}
```
-
+
-```js title="index.js"
-import { heading, panel, text } from "@metamask/snaps-sdk"
+```typescript title="index.ts"
+import type { OnUpdateHandler } from "@metamask/snaps-sdk";
+import { heading, panel, text } from "@metamask/snaps-sdk";
-module.exports.onUpdate = async () => {
+export const onUpdate: OnUpdateHandler = async () => {
await snap.request({
method: "snap_dialog",
params: {
diff --git a/snaps/reference/snaps-api.md b/snaps/reference/snaps-api.md
index e6cfa2f55ea..d85837deee4 100644
--- a/snaps/reference/snaps-api.md
+++ b/snaps/reference/snaps-api.md
@@ -48,8 +48,34 @@ Return value depends on the dialog `type`:
#### Example
-```javascript title="index.js"
-import { panel, text, heading } from "@metamask/snaps-sdk"
+
+
+
+```tsx title="index.tsx"
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
+
+const walletAddress = await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "prompt",
+ content: (
+
+ What is the wallet address?
+ Please enter the wallet address to be monitored
+
+ ),
+ placeholder: "0x123...",
+ },
+});
+
+// walletAddress will be a string containing the address entered by the user.
+```
+
+
+
+
+```typescript title="index.ts"
+import { panel, text, heading } from "@metamask/snaps-sdk";
const walletAddress = await snap.request({
method: "snap_dialog",
@@ -66,6 +92,9 @@ const walletAddress = await snap.request({
// walletAddress will be a string containing the address entered by the user.
```
+
+
+
## `snap_getBip32Entropy`
:::danger important
@@ -465,8 +494,37 @@ The user's locale setting as a [language code](https://github.com/MetaMask/metam
#### Example
-```javascript title="index.js"
-import { panel, text } from "@metamask/snaps-sdk"
+
+
+
+```tsx title="index.tsx"
+import { Box, Text } from "@metamask/snaps-sdk/jsx";
+
+const locale = await snap.request({ method: "snap_getLocale" });
+
+let greeting = "Hello";
+if(locale === "es") {
+ greeting = "Hola";
+}
+
+await snap.request({
+ method: "snap_dialog",
+ params: {
+ type: "alert",
+ content: (
+
+ {greeting}
+
+ ),
+ },
+});
+```
+
+
+
+
+```typescript title="index.ts"
+import { panel, text } from "@metamask/snaps-sdk";
const locale = await snap.request({ method: "snap_getLocale" })
@@ -484,6 +542,9 @@ await snap.request({
})
```
+
+
+
## `snap_manageAccounts`
Manages [account management Snap](../features/custom-evm-accounts/index.md) accounts.
diff --git a/src/theme/Tabs/index.tsx b/src/theme/Tabs/index.tsx
new file mode 100644
index 00000000000..d519f2d320c
--- /dev/null
+++ b/src/theme/Tabs/index.tsx
@@ -0,0 +1,177 @@
+import React, {cloneElement, type ReactElement} from 'react';
+import clsx from 'clsx';
+import {
+ useScrollPositionBlocker,
+ useTabs,
+ sanitizeTabsChildren,
+ type TabItemProps,
+} from '@docusaurus/theme-common/internal';
+import useIsBrowser from '@docusaurus/useIsBrowser';
+import type {Props} from '@theme/Tabs';
+import styles from './styles.module.css';
+
+function TabList({
+ className,
+ block,
+ selectedValue,
+ selectValue,
+ tabValues,
+}: Props & ReturnType) {
+ console.log(tabValues);
+ const tabRefs: (HTMLLIElement | null)[] = [];
+ const {blockElementScrollPositionUntilNextRender} =
+ useScrollPositionBlocker();
+
+ const handleTabChange = (
+ event:
+ | React.FocusEvent
+ | React.MouseEvent
+ | React.KeyboardEvent,
+ ) => {
+ const newTab = event.currentTarget;
+ const newTabIndex = tabRefs.indexOf(newTab);
+ const newTabValue = tabValues[newTabIndex]!.value;
+
+ if (newTabValue !== selectedValue) {
+ blockElementScrollPositionUntilNextRender(newTab);
+ selectValue(newTabValue);
+ }
+ };
+
+ const handleKeydown = (event: React.KeyboardEvent) => {
+ let focusElement: HTMLLIElement | null = null;
+
+ switch (event.key) {
+ case 'Enter': {
+ handleTabChange(event);
+ break;
+ }
+ case 'ArrowRight': {
+ const nextTab = tabRefs.indexOf(event.currentTarget) + 1;
+ focusElement = tabRefs[nextTab] ?? tabRefs[0]!;
+ break;
+ }
+ case 'ArrowLeft': {
+ const prevTab = tabRefs.indexOf(event.currentTarget) - 1;
+ focusElement = tabRefs[prevTab] ?? tabRefs[tabRefs.length - 1]!;
+ break;
+ }
+ default:
+ break;
+ }
+
+ focusElement?.focus();
+ };
+
+ return (
+
+ );
+}
+
+function TabsComponent(props: Props): JSX.Element {
+ const tabs = useTabs(props);
+ /**
+ * Tabs and TabItems aren't rendered in a straightforward manner.
+ * Swizzling the TabItem component has no effect: the TabItem is used only for its data.
+ * The actual tab items are rendered by the TabList component above.
+ * Here we are adding custom styling to a tab by combining the tab values from the hook
+ * with the props of the TabItem.
+ */
+ const finalTabs = {
+ ...tabs,
+ tabValues: tabs.tabValues.map((tabValue, idx) => ({
+ ...tabValue,
+ attributes: {
+ ...tabValue.attributes,
+ className: clsx(
+ tabValue.attributes?.className,
+ {
+ [styles.flaskOnly]: props.children[idx]?.props.flaskOnly,
+ [styles.deprecated]: props.children[idx]?.props.deprecated
+ }
+ )
+ }
+ }))
+ }
+ return (
+