| description | Create your own JSX components to improve readability. |
|---|---|
| sidebar_position | 5 |
When using Custom UI with JSX, you can create your own components by composing existing components or other user-defined components.
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.
import { Box, Heading, Text } from "@metamask/snaps-sdk/jsx";
export const Home = () => {
return (
<Box>
<Heading>Welcome to my Snap</Heading>
<Text>Hello, world!</Text>
</Box>
);
};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:
import { Home } from "./Home";
export const onHomepage = () => {
return <Home />;
};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:
export const Insight = (props) => {
return (
<Box>
<Row label="From">
<Address address={props.from} />
</Row>
<Row label="To">
{to ? <Address address={props.to} /> : <Text>None</Text>}
</Row>
</Box>
);
};This example contains two usages of props:
- The
Insightcomponent accepts apropsparameter, which is an object containing thefromandtoaddresses. Thefromaddress is accessed usingprops.from, and thetoaddress is accessed usingprops.to, sincepropsis just a regular JavaScript object. - The
Insightcomponent then uses the built-inAddresscomponent to display addresses. TheAddresscomponent accepts anaddressprop. When using theAddresscomponent, 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:
import { Insight } from "./Insight";
export const onTransaction = ({ transaction }) => {
return { content: <Insight from={transaction.from} to={transaction.to} /> };
};You can also access props using destructuring. This is not specific to JSX, but a feature of JavaScript:
export const Insight = ({ from, to }) => {
return (
<Box>
<Row label="From">
<Address address={from} />
</Row>
<Row label="To">
{to ? <Address address={to} /> : <Text>None</Text>}
</Row>
</Box>
);
};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:
export const Insight = ({ from, to }) => {
// This causes a syntax error
return (
<Row label="From">
<Address address={from} />
</Row>
<Row label="To">
{to ? <Address address={to} /> : <Text>None</Text>}
</Row>
);
};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:
export const Accounts = ({ accounts }) => {
return (
<Box>
<Heading>Accounts</Heading>
{accounts.map((account) => (
<Row label={account.name}>
<Address address={account.address} />
</Row>
))}
</Box>
);
};To use the Accounts component, you can pass an array of accounts as props:
import { Accounts } from "./Accounts";
export const onHomepage = () => {
const accounts = [
{
name: "Account 1",
address: "0x6827b8f6cc60497d9bf5210d602C0EcaFDF7C405"
},
{
name: "Account 2",
address: "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
}
];
return <Accounts accounts={accounts} />;
};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:
export const Account = ({ name, address }) => {
return <Row label={name}>
<Address address={address} />
</Row>
};Instead of writing:
const myAccount = {
name: "Account 1",
address: "0x6827b8f6cc60497d9bf5210d602C0EcaFDF7C405"
};
// ...
return <Account name={myAccount.name} address={myAccount.address} />You can write:
return <Account {...myAccount} />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:
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<InsightProps> = ({ from, to }) => {
return (
<Box>
<Row label="From">
<Address address={from as `0x${string}`} />
</Row>
<Row label="To">
{to ? <Address address={to as `0x${string}`} /> : <Text>None</Text>}
</Row>
</Box>
);
};Use the following steps to create user-defined components with TypeScript:
-
Import the
SnapComponenttype:import type { SnapComponent } from "@metamask/snaps-sdk/jsx";
-
Define a type for the props of your component. For example:
type InsightProps = { from: string; to?: string; };
-
Annotate the type of your component. For example:
export const Insight: SnapComponent<InsightProps> = ({ from, to }) => { // ... };
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
Insightcomponent without thefromprop, or passing anumberinstead of astringfor thefromprop results in a type error.