import { MediaCard } from '@coinbase/cds-web/cards/MediaCard'
MediaCard provides a contained card layout with optional media, ideal for showcasing assets, products, or promotional content. It replaces the deprecated FloatingAssetCard and ContainedAssetCard components.
Basic
At minimum, provide a thumbnail to display visual content and a title for the card heading.
<VStack gap={2}> <MediaCard thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="Title" subtitle="Subtitle" description="Description" width={320} /> <MediaCard thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="Title" subtitle="Subtitle" description="Description" width={320} media={ <RemoteImage alt="Media" height="100%" resizeMode="cover" shape="rectangle" src={ethBackground} width="100%" /> } /> </VStack>
Media Placement
Use the media prop to display larger visual content. Control its position with mediaPlacement:
start: Media on the leftend(default): Media on the right
<VStack gap={2}> <MediaCard thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="Title" subtitle="Subtitle" description="Description" width={320} media={ <RemoteImage alt="Media" height="100%" resizeMode="cover" shape="rectangle" src={ethBackground} width="100%" /> } mediaPlacement="start" /> <MediaCard thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="Title" subtitle="Subtitle" description="Description" width={320} media={ <RemoteImage alt="Media" height="100%" resizeMode="cover" shape="rectangle" src={ethBackground} width="100%" /> } mediaPlacement="end" /> </VStack>
Polymorphic and Interactive
MediaCard supports polymorphic rendering and can be made interactive with renderAsPressable. Use as to change the underlying element.
<VStack gap={2}> <MediaCard as="article" thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="Article Card" subtitle="article element" description="This card renders as an article element" width={320} media={ <RemoteImage alt="Media" height="100%" resizeMode="cover" shape="rectangle" src={ethBackground} width="100%" /> } /> <MediaCard renderAsPressable as="a" href="https://www.coinbase.com" thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="Interactive Card" subtitle="Link" description="Clickable card with href" width={320} media={ <RemoteImage alt="Media" height="100%" resizeMode="cover" shape="rectangle" src={ethBackground} width="100%" /> } /> <MediaCard renderAsPressable as="button" onClick={() => alert('Card clicked!')} thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="Interactive Card" subtitle="Button" description="Clickable card with onClick handler" width={320} media={ <RemoteImage alt="Media" height="100%" resizeMode="cover" shape="rectangle" src={ethBackground} width="100%" /> } /> </VStack>
Text Content
Long Text
The card handles long text content with truncation.
<MediaCard renderAsPressable as="button" thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="This is a very long title text that will get truncated" subtitle="This is a very long subtitle text that will get truncated" description="This is a very long description text that demonstrates how the card handles longer content" width={320} media={ <RemoteImage alt="Media" height="100%" resizeMode="cover" shape="rectangle" src={ethBackground} width="100%" /> } />
Custom Content
Use React nodes for custom styled text content.
<MediaCard thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title={ <Text as="p" font="title3"> Custom Title </Text> } subtitle={ <Text as="p" font="headline" color="fgPositive"> Custom Subtitle </Text> } description={ <Text as="p" font="label2"> Custom description with <strong>bold text</strong> and <em>italic text</em> </Text> } width={320} media={ <RemoteImage alt="Media" height="100%" resizeMode="cover" shape="rectangle" src={ethBackground} width="100%" /> } />
Styling
Use styles and classNames props to customize specific parts of the card.
<VStack gap={2}> <MediaCard thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="Title" subtitle="Subtitle" description="Description" width={320} media={ <RemoteImage alt="Media" height="100%" resizeMode="cover" shape="rectangle" src={ethBackground} width="100%" /> } styles={{ layoutContainer: { gap: 3 }, contentContainer: { padding: 3, gap: 2 }, textContainer: { gap: 1 }, headerContainer: { gap: 1 }, mediaContainer: { borderRadius: 300 }, }} /> <MediaCard thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="Title" subtitle="Subtitle" description="Description" width={320} media={ <RemoteImage alt="Media" height="100%" resizeMode="cover" shape="rectangle" src={ethBackground} width="100%" /> } styles={{ root: { borderWidth: 2, borderColor: 'blue' }, }} /> </VStack>
Multiple Cards
Display multiple cards in a carousel.
<Carousel styles={{ carousel: { gap: 16 } }}> <CarouselItem id="card1"> <MediaCard as="article" thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="Title" subtitle="Subtitle" description="Description" width={320} media={ <RemoteImage alt="Media" height="100%" resizeMode="cover" shape="rectangle" src={ethBackground} width="100%" /> } /> </CarouselItem> <CarouselItem id="card2"> <MediaCard renderAsPressable as="a" href="https://www.coinbase.com" thumbnail={<RemoteImage alt="Bitcoin" shape="circle" size="l" source={assets.btc.imageUrl} />} title="Bitcoin" subtitle="BTC" description="Another card with different content" width={320} media={ <RemoteImage alt="Media" height="100%" resizeMode="cover" shape="rectangle" src={ethBackground} width="100%" /> } /> </CarouselItem> <CarouselItem id="card3"> <MediaCard renderAsPressable as="button" onClick={() => console.log('clicked')} thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="Ethereum" subtitle="ETH" description="Card with onClick handler" width={320} /> </CarouselItem> </Carousel>
Migration from Deprecated Components
Migrating from ContainedAssetCard
Replace ContainedAssetCard with MediaCard:
// Before
<ContainedAssetCard
header={<RemoteImage source={assets.btc.imageUrl} width="32px" height="32px" />}
title="$309.43"
subtitle="Bitcoin"
description={<Text color="fgPositive">↗3.37%</Text>}
size="l"
>
<RemoteImage source={ethBackground} ... />
</ContainedAssetCard>
// After
<MediaCard
thumbnail={<RemoteImage source={assets.btc.imageUrl} shape="circle" size="l" />}
title="$309.43"
subtitle="Bitcoin"
description={<Text color="fgPositive">↗3.37%</Text>}
media={<RemoteImage src={ethBackground} height="100%" width="100%" resizeMode="cover" />}
mediaPlacement="end"
/>
Migrating from FloatingAssetCard
Replace FloatingAssetCard with MediaCard. Note that the floating variation (media outside the card container) is no longer supported:
// Before
<FloatingAssetCard
title="Balancing the Air"
subtitle="Amber V's Artwork"
description="0.5 ETH"
media={<RemoteImage source="/img/nft.png" ... />}
/>
// After
<MediaCard
thumbnail={<RemoteImage source="/img/nft.png" shape="circle" size="l" />}
title="Balancing the Air"
subtitle="Amber V's Artwork"
description="0.5 ETH"
/>