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 00000000000..b1b6b79c7a5
--- /dev/null
+++ b/snaps/features/custom-ui/user-defined-components.md
@@ -0,0 +1,258 @@
+---
+description: Create your own JSX components to improve readability.
+sidebar_position: 5
+---
+
+# 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.
+
+```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, 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";
+
+export const onHomepage = () => {
+ return ;
+};
+```
+
+## Example with props
+
+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) => {
+ return (
+
+
+
+
+
+ {to ? : None}
+
+
+ );
+};
+```
+
+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, 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:
+
+```jsx title="index.jsx"
+import { Insight } from "./Insight";
+
+export const onTransaction = ({ transaction }) => {
+ return { content: };
+};
+```
+
+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 }) => {
+ 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, 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 }) => {
+
+ // This causes a syntax error
+ return (
+
+
+
+
+ {to ? : None}
+
+ );
+};
+```
+
+## Return a list
+
+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 }) => {
+ 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 = () => {
+ const accounts = [
+ {
+ name: "Account 1",
+ address: "0x6827b8f6cc60497d9bf5210d602C0EcaFDF7C405"
+ },
+ {
+ name: "Account 2",
+ address: "0x71C7656EC7ab88b098defB751B7401B5f6d8976F"
+ }
+ ];
+ return ;
+};
+```
+
+## 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 write:
+
+```jsx
+return
+```
+
+## Use with TypeScript
+
+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";
+
+type InsightProps = {
+ from: string;
+ to?: string;
+};
+
+export const Insight: SnapComponent = ({ from, to }) => {
+ return (
+
+
+
+
+
+ {to ? : None}
+
+
+ );
+};
+```
+
+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";
+ ```
+
+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.
+ For example:
+
+ ```tsx
+ export const Insight: SnapComponent = ({ 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 `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 432f84313ab..aec606c6b83 100644
--- a/snaps/features/custom-ui/with-jsx.md
+++ b/snaps/features/custom-ui/with-jsx.md
@@ -712,7 +712,7 @@ module.exports.onHomePage = async () => {
-## Emojis
+### Emojis
Text-based components (such as [`Heading`](#heading) and [`Text`](#text)) accept emojis.
@@ -738,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).