Skip to content

Commit 8e0f77d

Browse files
committed
add asChild prop to UI components
1 parent 1ee0dff commit 8e0f77d

File tree

17 files changed

+113
-77
lines changed

17 files changed

+113
-77
lines changed

.changeset/funny-taxis-grow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"thirdweb": patch
3+
---
4+
5+
allow passing `asChild` for most ui components

packages/thirdweb/package.json

Lines changed: 20 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -132,63 +132,25 @@
132132
},
133133
"typesVersions": {
134134
"*": {
135-
"adapters/*": [
136-
"./dist/types/exports/adapters/*.d.ts"
137-
],
138-
"auth": [
139-
"./dist/types/exports/auth.d.ts"
140-
],
141-
"chains": [
142-
"./dist/types/exports/chains.d.ts"
143-
],
144-
"contract": [
145-
"./dist/types/exports/contract.d.ts"
146-
],
147-
"deploys": [
148-
"./dist/types/exports/deploys.d.ts"
149-
],
150-
"event": [
151-
"./dist/types/exports/event.d.ts"
152-
],
153-
"extensions/*": [
154-
"./dist/types/exports/extensions/*.d.ts"
155-
],
156-
"pay": [
157-
"./dist/types/exports/pay.d.ts"
158-
],
159-
"react": [
160-
"./dist/types/exports/react.d.ts"
161-
],
162-
"react-native": [
163-
"./dist/types/exports/react-native.d.ts"
164-
],
165-
"rpc": [
166-
"./dist/types/exports/rpc.d.ts"
167-
],
168-
"storage": [
169-
"./dist/types/exports/storage.d.ts"
170-
],
171-
"transaction": [
172-
"./dist/types/exports/transaction.d.ts"
173-
],
174-
"utils": [
175-
"./dist/types/exports/utils.d.ts"
176-
],
177-
"wallets": [
178-
"./dist/types/exports/wallets.d.ts"
179-
],
180-
"wallets/*": [
181-
"./dist/types/exports/wallets/*.d.ts"
182-
],
183-
"modules": [
184-
"./dist/types/exports/modules.d.ts"
185-
],
186-
"social": [
187-
"./dist/types/exports/social.d.ts"
188-
],
189-
"ai": [
190-
"./dist/types/exports/ai.d.ts"
191-
]
135+
"adapters/*": ["./dist/types/exports/adapters/*.d.ts"],
136+
"auth": ["./dist/types/exports/auth.d.ts"],
137+
"chains": ["./dist/types/exports/chains.d.ts"],
138+
"contract": ["./dist/types/exports/contract.d.ts"],
139+
"deploys": ["./dist/types/exports/deploys.d.ts"],
140+
"event": ["./dist/types/exports/event.d.ts"],
141+
"extensions/*": ["./dist/types/exports/extensions/*.d.ts"],
142+
"pay": ["./dist/types/exports/pay.d.ts"],
143+
"react": ["./dist/types/exports/react.d.ts"],
144+
"react-native": ["./dist/types/exports/react-native.d.ts"],
145+
"rpc": ["./dist/types/exports/rpc.d.ts"],
146+
"storage": ["./dist/types/exports/storage.d.ts"],
147+
"transaction": ["./dist/types/exports/transaction.d.ts"],
148+
"utils": ["./dist/types/exports/utils.d.ts"],
149+
"wallets": ["./dist/types/exports/wallets.d.ts"],
150+
"wallets/*": ["./dist/types/exports/wallets/*.d.ts"],
151+
"modules": ["./dist/types/exports/modules.d.ts"],
152+
"social": ["./dist/types/exports/social.d.ts"],
153+
"ai": ["./dist/types/exports/ai.d.ts"]
192154
}
193155
},
194156
"browser": {
@@ -217,6 +179,7 @@
217179
"@radix-ui/react-dialog": "1.1.5",
218180
"@radix-ui/react-focus-scope": "1.1.1",
219181
"@radix-ui/react-icons": "1.3.2",
182+
"@radix-ui/react-slot": "1.1.1",
220183
"@radix-ui/react-tooltip": "1.1.7",
221184
"@tanstack/react-query": "5.65.1",
222185
"@walletconnect/ethereum-provider": "2.17.5",

packages/thirdweb/src/react/web/ui/SiteLink.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
"use client";
2+
import * as Slot from "@radix-ui/react-slot";
23
import { useQuery } from "@tanstack/react-query";
34
import type { ThirdwebClient } from "../../../client/client.js";
45
import { getLastAuthProvider } from "../../../react/core/utils/storage.js";
@@ -34,8 +35,10 @@ export function SiteLink({
3435
client,
3536
ecosystem,
3637
children,
38+
asChild,
3739
...props
3840
}: {
41+
asChild?: boolean;
3942
href: string;
4043
client: ThirdwebClient;
4144
ecosystem?: Ecosystem;
@@ -82,9 +85,10 @@ export function SiteLink({
8285
url.searchParams.set("authCookie", authCookie);
8386
}
8487

88+
const Comp = asChild ? Slot.Root : "a";
8589
return (
86-
<a href={encodeURI(url.toString())} {...props}>
90+
<Comp href={encodeURI(url.toString())} {...props}>
8791
{children}
88-
</a>
92+
</Comp>
8993
);
9094
}

packages/thirdweb/src/react/web/ui/prebuilt/Account/address.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22

3+
import * as Slot from "@radix-ui/react-slot";
34
import { useAccountContext } from "../../../../core/account/provider.js";
45

56
/**
@@ -8,6 +9,7 @@ import { useAccountContext } from "../../../../core/account/provider.js";
89
*/
910
export interface AccountAddressProps
1011
extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
12+
asChild?: boolean;
1113
/**
1214
* The function used to transform (format) the wallet address
1315
* Specifically useful for shortening the wallet.
@@ -56,9 +58,11 @@ export interface AccountAddressProps
5658
*/
5759
export function AccountAddress({
5860
formatFn,
61+
asChild,
5962
...restProps
6063
}: AccountAddressProps) {
6164
const { address } = useAccountContext();
6265
const value = formatFn ? formatFn(address) : address;
63-
return <span {...restProps}>{value}</span>;
66+
const Comp = asChild ? Slot.Root : "span";
67+
return <Comp {...restProps}>{value}</Comp>;
6468
}

packages/thirdweb/src/react/web/ui/prebuilt/Account/avatar.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22

3+
import * as Slot from "@radix-ui/react-slot";
34
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
45
import type React from "react";
56
import type { JSX } from "react";
@@ -20,6 +21,7 @@ import { useAccountContext } from "../../../../core/account/provider.js";
2021
export interface AccountAvatarProps
2122
extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, "src">,
2223
Omit<ResolveNameOptions, "client" | "address"> {
24+
asChild?: boolean;
2325
/**
2426
* Use this prop to prioritize the social profile that you want to display
2527
* This is useful for a wallet containing multiple social profiles.
@@ -159,6 +161,7 @@ export function AccountAvatar({
159161
loadingComponent,
160162
fallbackComponent,
161163
queryOptions,
164+
asChild,
162165
...restProps
163166
}: AccountAvatarProps) {
164167
const { address, client } = useAccountContext();
@@ -222,5 +225,6 @@ export function AccountAvatar({
222225
return fallbackComponent || null;
223226
}
224227

225-
return <img src={avatarQuery.data} {...restProps} alt={restProps.alt} />;
228+
const Comp = asChild ? Slot.Root : "img";
229+
return <Comp src={avatarQuery.data} {...restProps} alt={restProps.alt} />;
226230
}

packages/thirdweb/src/react/web/ui/prebuilt/Account/balance.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22

3+
import * as Slot from "@radix-ui/react-slot";
34
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
45
import type React from "react";
56
import type { JSX } from "react";
@@ -22,6 +23,7 @@ import { formatAccountTokenBalance } from "../../../../core/utils/account.js";
2223
*/
2324
export interface AccountBalanceProps
2425
extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
26+
asChild?: boolean;
2527
/**
2628
* The network to fetch balance on
2729
* If not passed, the component will use the current chain that the wallet is connected to (`useActiveWalletChain()`)
@@ -166,6 +168,7 @@ export function AccountBalance({
166168
queryOptions,
167169
formatFn,
168170
showBalanceInFiat,
171+
asChild,
169172
...restProps
170173
}: AccountBalanceProps) {
171174
const { address, client } = useAccountContext();
@@ -212,12 +215,13 @@ export function AccountBalance({
212215
);
213216
}
214217

218+
const Comp = asChild ? Slot.Root : "span";
215219
return (
216-
<span {...restProps}>
220+
<Comp {...restProps}>
217221
{formatAccountTokenBalance({
218222
...balanceQuery.data,
219223
decimals: balanceQuery.data.balance < 1 ? 3 : 2,
220224
})}
221-
</span>
225+
</Comp>
222226
);
223227
}

packages/thirdweb/src/react/web/ui/prebuilt/Account/name.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22

3+
import * as Slot from "@radix-ui/react-slot";
34
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
45
import type React from "react";
56
import type { JSX } from "react";
@@ -19,6 +20,7 @@ import { useAccountContext } from "../../../../core/account/provider.js";
1920
export interface AccountNameProps
2021
extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children">,
2122
Omit<ResolveNameOptions, "client" | "address"> {
23+
asChild?: boolean;
2224
/**
2325
* A function used to transform (format) the name of the account.
2426
* it should take in a string and output a string.
@@ -134,6 +136,7 @@ export function AccountName({
134136
queryOptions,
135137
loadingComponent,
136138
fallbackComponent,
139+
asChild,
137140
...restProps
138141
}: AccountNameProps) {
139142
const { address, client } = useAccountContext();
@@ -177,5 +180,6 @@ export function AccountName({
177180
return fallbackComponent || null;
178181
}
179182

180-
return <span {...restProps}>{nameQuery.data}</span>;
183+
const Comp = asChild ? Slot.Root : "span";
184+
return <Comp {...restProps}>{nameQuery.data}</Comp>;
181185
}

packages/thirdweb/src/react/web/ui/prebuilt/Chain/icon.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22

3+
import * as Slot from "@radix-ui/react-slot";
34
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
45
import type { JSX } from "react";
56
import type { Chain } from "../../../../../chains/types.js";
@@ -16,6 +17,7 @@ import { useChainContext } from "./provider.js";
1617
*/
1718
export interface ChainIconProps
1819
extends Omit<React.ImgHTMLAttributes<HTMLImageElement>, "src"> {
20+
asChild?: boolean;
1921
/**
2022
* You need a ThirdwebClient to resolve the icon which is hosted on IPFS.
2123
* (since most chain icons are hosted on IPFS, loading them via thirdweb gateway will ensure better performance)
@@ -119,6 +121,7 @@ export function ChainIcon({
119121
fallbackComponent,
120122
queryOptions,
121123
client,
124+
asChild,
122125
...restProps
123126
}: ChainIconProps) {
124127
const { chain } = useChainContext();
@@ -136,7 +139,8 @@ export function ChainIcon({
136139
return fallbackComponent || null;
137140
}
138141

139-
return <img src={iconQuery.data} {...restProps} alt={restProps.alt} />;
142+
const Comp = asChild ? Slot.Root : "img";
143+
return <Comp src={iconQuery.data} {...restProps} alt={restProps.alt} />;
140144
}
141145

142146
/**

packages/thirdweb/src/react/web/ui/prebuilt/Chain/name.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22

3+
import * as Slot from "@radix-ui/react-slot";
34
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
45
import type React from "react";
56
import type { JSX } from "react";
@@ -15,6 +16,7 @@ import { useChainContext } from "./provider.js";
1516
*/
1617
export interface ChainNameProps
1718
extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
19+
asChild?: boolean;
1820
/**
1921
* This prop can be a string or a (async) function that resolves to a string, representing the name of the chain
2022
* This is particularly useful if you already have a way to fetch the chain name.
@@ -153,6 +155,7 @@ export function ChainName({
153155
loadingComponent,
154156
fallbackComponent,
155157
queryOptions,
158+
asChild,
156159
...restProps
157160
}: ChainNameProps) {
158161
const { chain } = useChainContext();
@@ -172,7 +175,8 @@ export function ChainName({
172175

173176
const displayValue = formatFn ? formatFn(nameQuery.data) : nameQuery.data;
174177

175-
return <span {...restProps}>{displayValue}</span>;
178+
const Comp = asChild ? Slot.Root : "span";
179+
return <Comp {...restProps}>{displayValue}</Comp>;
176180
}
177181

178182
/**

packages/thirdweb/src/react/web/ui/prebuilt/NFT/description.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
"use client";
22

3+
import * as Slot from "@radix-ui/react-slot";
34
import { type UseQueryOptions, useQuery } from "@tanstack/react-query";
45
import type { JSX } from "react";
56
import type { ThirdwebContract } from "../../../../../contract/contract.js";
@@ -9,6 +10,7 @@ import { getNFTInfo } from "./utils.js";
910

1011
export interface NFTDescriptionProps
1112
extends Omit<React.HTMLAttributes<HTMLSpanElement>, "children"> {
13+
asChild?: boolean;
1214
loadingComponent?: JSX.Element;
1315
fallbackComponent?: JSX.Element;
1416
/**
@@ -88,6 +90,7 @@ export function NFTDescription({
8890
fallbackComponent,
8991
queryOptions,
9092
descriptionResolver,
93+
asChild,
9194
...restProps
9295
}: NFTDescriptionProps) {
9396
const { contract, tokenId } = useNFTContext();
@@ -119,7 +122,8 @@ export function NFTDescription({
119122
return fallbackComponent || null;
120123
}
121124

122-
return <span {...restProps}>{descQuery.data}</span>;
125+
const Comp = asChild ? Slot.Root : "span";
126+
return <Comp {...restProps}>{descQuery.data}</Comp>;
123127
}
124128

125129
/**

0 commit comments

Comments
 (0)