From 30aca146c7a5bace2bbc777660b6e89a5973c26c Mon Sep 17 00:00:00 2001
From: Ziad Saab
Date: Tue, 17 Sep 2024 14:33:18 -0500
Subject: [PATCH 1/9] Add user-defined components how-to
---
.../custom-ui/user-defined-components.md | 194 ++++++++++++++++++
1 file changed, 194 insertions(+)
create mode 100644 snaps/features/custom-ui/user-defined-components.md
diff --git a/snaps/features/custom-ui/user-defined-components.md b/snaps/features/custom-ui/user-defined-components.md
new file mode 100644
index 0000000000..b0083d75b5
--- /dev/null
+++ b/snaps/features/custom-ui/user-defined-components.md
@@ -0,0 +1,194 @@
+---
+description: Create your own JSX components to improve readability.
+sidebar_position: 5
+---
+
+# User-defined components
+
+When using JSX, you can create your own components by composing [existing components](with-jsx.md), or
+other user-defined components.
+
+## Basic example
+
+In this first, basic example, the user-defined component is static. It does not accept any props (parameters) and returns the contents of a static home page.
+
+```jsx title="Home.jsx"
+import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
+
+export const Home = () => {
+ return (
+
+ Welcome to my Snap
+ Hello, world!
+
+ );
+};
+```
+
+Once the component is defined, it can be used anywhere in the Snap. For example, to display the home page, you can use the following code:
+
+```jsx title="index.jsx"
+import { Home } from "./Home";
+
+export const onHomepage = () => {
+ return ;
+};
+```
+
+## Example with props
+
+Components can be parametrized by passing props. Props are passed to the component as an object and can be accessed using the first parameter of the component's definition function:
+
+```jsx title="Insight.jsx"
+export const Insight = (props) => {
+ return (
+
+
+
+
+
+ {to ? : None}
+
+
+ );
+};
+```
+
+In the example above, we see two usages of props:
+
+- The `Insight` component accepts a `props` parameter, which is an object containing the `from` and `to` addresses. The `from` address is accessed using `props.from`, and the `to` address is accessed using `props.to`, since `props` is just a regular JavaScript object.
+- The `Insight` component then uses the built-in `Address` component to display addresses. The `Address` component accepts an `address` prop. When using the `Address` component, we pass props to it by using a notation similar to HTML attributes: `address={props.from}`.
+
+To use the `Insight` component, you can pass the `from` and `to` addresses as props:
+
+```jsx title="index.jsx"
+import { Insight } from "./Insight";
+
+export const onTransaction = () => {
+ return { content: };
+};
+```
+
+Props can be accessed using destructuring as well. This is not specific to JSX, simply a feature of JavaScript:
+
+```jsx title="Insight.jsx"
+export const Insight = ({ from, to }) => {
+ return (
+
+
+
+
+
+ {to ? : None}
+
+
+ );
+};
+```
+
+## Return multiple elements
+
+A JSX expression can only contain a single root element. To return multiple elements, wrap them in a parent element,
+like `Box`. In the example above, we wrap the two `Row` elements in a `Box` element. Trying to return multiple elements
+without a parent element will result in a syntax error.
+
+```jsx title="WRONG-Insight.jsx"
+export const Insight = ({ from, to }) => {
+
+ // This causes a syntax error
+ return (
+
+
+
+
+ {to ? : None}
+
+ );
+};
+```
+
+## Return a list
+
+To return a list of elements, you can use an array. In the example below, the `Accounts` components receives an
+array of accounts as props, and uses the array to display a list of accounts using `Array.map`:
+
+```jsx title="Accounts.jsx"
+export const Accounts = ({ accounts }) => {
+ return (
+
+ Accounts
+ {accounts.map((account) => (
+
+
+
+ ))}
+
+ );
+};
+```
+
+To use the `Accounts` component, you can pass an array of accounts as props:
+
+```jsx title="index.jsx"
+import { Accounts } from "./Accounts";
+
+export const onHomepage = () => {
+ return ;
+};
+```
+
+## Usage with TypeScript
+
+The `@metamask/snaps-sdk/jsx` package exports a `SnapComponent` type that can be used to define components that are compatible with TypeScript. The `SnapComponent` type is generic: it accepts a `Props` type parameter that will define the shape of the props object. For example:
+
+```tsx title="Insight.tsx"
+import type { SnapComponent } from '@metamask/snaps-sdk/jsx';
+import { Button, Box, Text, Row, Address } from '@metamask/snaps-sdk/jsx';
+
+type InsightProps = {
+ from: string;
+ to?: string;
+};
+
+export const Insight: SnapComponent = ({ from, to }) => {
+ return (
+
+
+
+
+
+ {to ? : None}
+
+
+
+ );
+};
+```
+
+Here are the steps to create user-defined components with TypeScript:
+
+1. Import the `SnapComponent` type:
+ ```tsx
+ import type { SnapComponent } from '@metamask/snaps-sdk/jsx';
+ ```
+2. Define a type for the props of your component:
+ ```tsx
+ type InsightProps = {
+ from: string;
+ to?: string;
+ };
+ ``
+
+3. Annotate the type of your component:
+ ```tsx
+ export const Insight: SnapComponent = ({ from, to }) => {
+ // ...
+ };
+ ```
+
+This will have two effects:
+
+- It will allow TypeScript to infer the types of the props inside your component.
+- It will make sure that the props passed to the component match the expected props. For example,
+using the `Insight` component above without the `from` prop, or passing a `number` instead of a
+`string` for the `from` prop will result in a type error.
\ No newline at end of file
From 6b78488593596ac6b636875a7636c53b47dc0edf Mon Sep 17 00:00:00 2001
From: Ziad Saab
Date: Tue, 17 Sep 2024 14:38:32 -0500
Subject: [PATCH 2/9] Properly indent code samples in list
---
.../custom-ui/user-defined-components.md | 29 +++++++++----------
1 file changed, 14 insertions(+), 15 deletions(-)
diff --git a/snaps/features/custom-ui/user-defined-components.md b/snaps/features/custom-ui/user-defined-components.md
index b0083d75b5..367e7ab363 100644
--- a/snaps/features/custom-ui/user-defined-components.md
+++ b/snaps/features/custom-ui/user-defined-components.md
@@ -168,23 +168,22 @@ export const Insight: SnapComponent = ({ from, to }) => {
Here are the steps to create user-defined components with TypeScript:
1. Import the `SnapComponent` type:
- ```tsx
- import type { SnapComponent } from '@metamask/snaps-sdk/jsx';
- ```
+ ```tsx
+ import type { SnapComponent } from '@metamask/snaps-sdk/jsx';
+ ```
2. Define a type for the props of your component:
- ```tsx
- type InsightProps = {
- from: string;
- to?: string;
- };
- ``
-
+ ```tsx
+ type InsightProps = {
+ from: string;
+ to?: string;
+ };
+ ```
3. Annotate the type of your component:
- ```tsx
- export const Insight: SnapComponent = ({ from, to }) => {
- // ...
- };
- ```
+ ```tsx
+ export const Insight: SnapComponent = ({ from, to }) => {
+ // ...
+ };
+ ```
This will have two effects:
From 063b97831c1c25e89b4b67fe7cc2e77abd0163b4 Mon Sep 17 00:00:00 2001
From: Ziad Saab
Date: Tue, 17 Sep 2024 14:42:58 -0500
Subject: [PATCH 3/9] link from JSX component page to user-defined components
page
---
snaps/features/custom-ui/with-jsx.md | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/snaps/features/custom-ui/with-jsx.md b/snaps/features/custom-ui/with-jsx.md
index 432f84313a..59ecf5bedf 100644
--- a/snaps/features/custom-ui/with-jsx.md
+++ b/snaps/features/custom-ui/with-jsx.md
@@ -712,6 +712,10 @@ module.exports.onHomePage = async () => {
+## User-defined components
+
+In addition to the components provided by the SDK, you can also [define your own components](user-defined-components.md).
+
## Emojis
Text-based components (such as [`Heading`](#heading) and [`Text`](#text)) accept emojis.
From 353cc2a05007572fb194043b92ba7f896e568fef Mon Sep 17 00:00:00 2001
From: Ziad Saab
Date: Tue, 17 Sep 2024 14:47:37 -0500
Subject: [PATCH 4/9] Remove Button from Insight tsx component
---
snaps/features/custom-ui/user-defined-components.md | 1 -
1 file changed, 1 deletion(-)
diff --git a/snaps/features/custom-ui/user-defined-components.md b/snaps/features/custom-ui/user-defined-components.md
index 367e7ab363..9ed67adaa0 100644
--- a/snaps/features/custom-ui/user-defined-components.md
+++ b/snaps/features/custom-ui/user-defined-components.md
@@ -159,7 +159,6 @@ export const Insight: SnapComponent = ({ from, to }) => {
{to ? : None}
-
);
};
From 4e435bd7e00e876b0876ae63e2ed8e8ff7ded940 Mon Sep 17 00:00:00 2001
From: Ziad Saab
Date: Tue, 17 Sep 2024 15:30:15 -0500
Subject: [PATCH 5/9] from > to
---
snaps/features/custom-ui/user-defined-components.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/snaps/features/custom-ui/user-defined-components.md b/snaps/features/custom-ui/user-defined-components.md
index 9ed67adaa0..fc44af2a41 100644
--- a/snaps/features/custom-ui/user-defined-components.md
+++ b/snaps/features/custom-ui/user-defined-components.md
@@ -157,7 +157,7 @@ export const Insight: SnapComponent = ({ from, to }) => {
- {to ? : None}
+ {to ? : None}
);
From dc6207de5f745d3e8d63077390fd79281fe78074 Mon Sep 17 00:00:00 2001
From: Ziad Saab
Date: Tue, 17 Sep 2024 15:42:12 -0500
Subject: [PATCH 6/9] Fix Address example
---
snaps/features/custom-ui/user-defined-components.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/snaps/features/custom-ui/user-defined-components.md b/snaps/features/custom-ui/user-defined-components.md
index fc44af2a41..6663118b9b 100644
--- a/snaps/features/custom-ui/user-defined-components.md
+++ b/snaps/features/custom-ui/user-defined-components.md
@@ -64,8 +64,8 @@ To use the `Insight` component, you can pass the `from` and `to` addresses as pr
```jsx title="index.jsx"
import { Insight } from "./Insight";
-export const onTransaction = () => {
- return { content: };
+export const onTransaction = ({ transaction }) => {
+ return { content: };
};
```
From f596380b73e12ee60ff730e9d50cf5d2b77e0365 Mon Sep 17 00:00:00 2001
From: Ziad Saab
Date: Tue, 17 Sep 2024 15:46:30 -0500
Subject: [PATCH 7/9] use valid ethereum addresses in examples
---
snaps/features/custom-ui/user-defined-components.md | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/snaps/features/custom-ui/user-defined-components.md b/snaps/features/custom-ui/user-defined-components.md
index 6663118b9b..dd9badd991 100644
--- a/snaps/features/custom-ui/user-defined-components.md
+++ b/snaps/features/custom-ui/user-defined-components.md
@@ -133,7 +133,17 @@ To use the `Accounts` component, you can pass an array of accounts as props:
import { Accounts } from "./Accounts";
export const onHomepage = () => {
- return ;
+ const accounts = [
+ {
+ name: "Account 1",
+ address: "0x6827b8f6cc60497d9bf5210d602C0EcaFDF7C405"
+ },
+ {
+ name: "Account 2",
+ address: "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
+ }
+ ];
+ return ;
};
```
From b7964ab8437f7e8cb471d70be127c3730980951b Mon Sep 17 00:00:00 2001
From: Ziad Saab
Date: Tue, 17 Sep 2024 15:51:16 -0500
Subject: [PATCH 8/9] add a section on spreading props
---
.../custom-ui/user-defined-components.md | 31 +++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/snaps/features/custom-ui/user-defined-components.md b/snaps/features/custom-ui/user-defined-components.md
index dd9badd991..eb98ca3cff 100644
--- a/snaps/features/custom-ui/user-defined-components.md
+++ b/snaps/features/custom-ui/user-defined-components.md
@@ -147,6 +147,37 @@ export const onHomepage = () => {
};
```
+## Spread props
+
+If an object has the same keys and value types as the props of a component, you can spread the
+object's properties as props for the component. For example, given the following component:
+
+```jsx title="Account.jsx"
+export const Account = ({ name, address }) => {
+ return
+
+
+};
+```
+
+Instead of writing:
+
+```jsx title="index.jsx"
+const myAccount = {
+ name: "Account 1",
+ address: "0x6827b8f6cc60497d9bf5210d602C0EcaFDF7C405"
+};
+
+// ...
+return
+```
+
+You can simply write:
+
+```jsx
+return
+```
+
## Usage with TypeScript
The `@metamask/snaps-sdk/jsx` package exports a `SnapComponent` type that can be used to define components that are compatible with TypeScript. The `SnapComponent` type is generic: it accepts a `Props` type parameter that will define the shape of the props object. For example:
From a870737821d8fecb6cbf17e5c22f769763df3a7c Mon Sep 17 00:00:00 2001
From: Alexandra Tran
Date: Wed, 18 Sep 2024 11:21:32 -0700
Subject: [PATCH 9/9] edit content
---
.../custom-ui/user-defined-components.md | 85 ++++++++++++-------
snaps/features/custom-ui/with-jsx.md | 10 +--
2 files changed, 60 insertions(+), 35 deletions(-)
diff --git a/snaps/features/custom-ui/user-defined-components.md b/snaps/features/custom-ui/user-defined-components.md
index eb98ca3cff..b1b6b79c7a 100644
--- a/snaps/features/custom-ui/user-defined-components.md
+++ b/snaps/features/custom-ui/user-defined-components.md
@@ -5,12 +5,13 @@ sidebar_position: 5
# User-defined components
-When using JSX, you can create your own components by composing [existing components](with-jsx.md), or
-other user-defined components.
+When using [Custom UI with JSX](with-jsx.md), you can create your own components by composing
+existing components or other user-defined components.
## Basic example
-In this first, basic example, the user-defined component is static. It does not accept any props (parameters) and returns the contents of a static home page.
+In this first, basic example, the user-defined component is static.
+It does not accept any props (parameters) and returns the contents of a static home page.
```jsx title="Home.jsx"
import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
@@ -25,7 +26,8 @@ export const Home = () => {
};
```
-Once the component is defined, it can be used anywhere in the Snap. For example, to display the home page, you can use the following code:
+Once the component is defined, you can use it anywhere in the Snap.
+For example, to display the home page, you can use the following code:
```jsx title="index.jsx"
import { Home } from "./Home";
@@ -37,7 +39,9 @@ export const onHomepage = () => {
## Example with props
-Components can be parametrized by passing props. Props are passed to the component as an object and can be accessed using the first parameter of the component's definition function:
+You can parameterize components by passing props.
+Props are passed to the component as an object and can be accessed using the first parameter of the
+component's definition function:
```jsx title="Insight.jsx"
export const Insight = (props) => {
@@ -54,10 +58,16 @@ export const Insight = (props) => {
};
```
-In the example above, we see two usages of props:
+This example contains two usages of props:
-- The `Insight` component accepts a `props` parameter, which is an object containing the `from` and `to` addresses. The `from` address is accessed using `props.from`, and the `to` address is accessed using `props.to`, since `props` is just a regular JavaScript object.
-- The `Insight` component then uses the built-in `Address` component to display addresses. The `Address` component accepts an `address` prop. When using the `Address` component, we pass props to it by using a notation similar to HTML attributes: `address={props.from}`.
+- The `Insight` component accepts a `props` parameter, which is an object containing the `from` and
+ `to` addresses.
+ The `from` address is accessed using `props.from`, and the `to` address is accessed using
+ `props.to`, since `props` is just a regular JavaScript object.
+- The `Insight` component then uses the built-in `Address` component to display addresses.
+ The `Address` component accepts an `address` prop.
+ When using the `Address` component, you can pass props to it using a notation similar to HTML
+ attributes: `address={props.from}`.
To use the `Insight` component, you can pass the `from` and `to` addresses as props:
@@ -69,7 +79,8 @@ export const onTransaction = ({ transaction }) => {
};
```
-Props can be accessed using destructuring as well. This is not specific to JSX, simply a feature of JavaScript:
+You can also access props using destructuring.
+This is not specific to JSX, but a feature of JavaScript:
```jsx title="Insight.jsx"
export const Insight = ({ from, to }) => {
@@ -88,9 +99,10 @@ export const Insight = ({ from, to }) => {
## Return multiple elements
-A JSX expression can only contain a single root element. To return multiple elements, wrap them in a parent element,
-like `Box`. In the example above, we wrap the two `Row` elements in a `Box` element. Trying to return multiple elements
-without a parent element will result in a syntax error.
+A JSX expression can only contain a single root element.
+To return multiple elements, wrap them in a parent element, such as `Box`.
+In the previous example, the two `Row` elements are wrapped in a `Box` element.
+Trying to return multiple elements without a parent element results in a syntax error:
```jsx title="WRONG-Insight.jsx"
export const Insight = ({ from, to }) => {
@@ -109,8 +121,9 @@ export const Insight = ({ from, to }) => {
## Return a list
-To return a list of elements, you can use an array. In the example below, the `Accounts` components receives an
-array of accounts as props, and uses the array to display a list of accounts using `Array.map`:
+To return a list of elements, you can use an array.
+In the following example, the `Accounts` components receives an array of accounts as props, and uses
+the array to display a list of accounts using `Array.map`:
```jsx title="Accounts.jsx"
export const Accounts = ({ accounts }) => {
@@ -150,7 +163,8 @@ export const onHomepage = () => {
## Spread props
If an object has the same keys and value types as the props of a component, you can spread the
-object's properties as props for the component. For example, given the following component:
+object's properties as props for the component.
+For example, given the following component:
```jsx title="Account.jsx"
export const Account = ({ name, address }) => {
@@ -172,19 +186,23 @@ const myAccount = {
return
```
-You can simply write:
+You can write:
```jsx
return
```
-## Usage with TypeScript
+## Use with TypeScript
-The `@metamask/snaps-sdk/jsx` package exports a `SnapComponent` type that can be used to define components that are compatible with TypeScript. The `SnapComponent` type is generic: it accepts a `Props` type parameter that will define the shape of the props object. For example:
+The `@metamask/snaps-sdk/jsx` package exports a `SnapComponent` type that you can use to define
+components that are compatible with TypeScript.
+The `SnapComponent` type is generic: it accepts a `Props` type parameter that defines the shape of
+the props object.
+For example:
```tsx title="Insight.tsx"
-import type { SnapComponent } from '@metamask/snaps-sdk/jsx';
-import { Button, Box, Text, Row, Address } from '@metamask/snaps-sdk/jsx';
+import type { SnapComponent } from "@metamask/snaps-sdk/jsx";
+import { Button, Box, Text, Row, Address } from "@metamask/snaps-sdk/jsx";
type InsightProps = {
from: string;
@@ -205,29 +223,36 @@ export const Insight: SnapComponent = ({ from, to }) => {
};
```
-Here are the steps to create user-defined components with TypeScript:
+Use the following steps to create user-defined components with TypeScript:
1. Import the `SnapComponent` type:
+
```tsx
- import type { SnapComponent } from '@metamask/snaps-sdk/jsx';
+ import type { SnapComponent } from "@metamask/snaps-sdk/jsx";
```
-2. Define a type for the props of your component:
+
+2. Define a type for the props of your component.
+ For example:
+
```tsx
type InsightProps = {
from: string;
to?: string;
};
```
-3. Annotate the type of your component:
+
+3. Annotate the type of your component.
+ For example:
+
```tsx
export const Insight: SnapComponent = ({ from, to }) => {
// ...
};
```
-This will have two effects:
-
-- It will allow TypeScript to infer the types of the props inside your component.
-- It will make sure that the props passed to the component match the expected props. For example,
-using the `Insight` component above without the `from` prop, or passing a `number` instead of a
-`string` for the `from` prop will result in a type error.
\ No newline at end of file
+ This has two effects:
+
+ - It allows TypeScript to infer the types of the props inside your component.
+ - It ensures that the props passed to the component match the expected props.
+ In this example, using the `Insight` component without the `from` prop, or passing a `number`
+ instead of a `string` for the `from` prop results in a type error.
diff --git a/snaps/features/custom-ui/with-jsx.md b/snaps/features/custom-ui/with-jsx.md
index 59ecf5bedf..aec606c6b8 100644
--- a/snaps/features/custom-ui/with-jsx.md
+++ b/snaps/features/custom-ui/with-jsx.md
@@ -712,11 +712,7 @@ module.exports.onHomePage = async () => {
-## User-defined components
-
-In addition to the components provided by the SDK, you can also [define your own components](user-defined-components.md).
-
-## Emojis
+### Emojis
Text-based components (such as [`Heading`](#heading) and [`Text`](#text)) accept emojis.
@@ -742,3 +738,7 @@ await snap.request({
+
+## User-defined components
+
+In addition to the components provided by the SDK, you can [define your own components](user-defined-components.md).