import { ContentCard } from '@coinbase/cds-web/cards/ContentCard'
ContentCard is a flexible, composable card component built with ContentCardHeader, ContentCardBody, and ContentCardFooter sub-components. It can display various content layouts including text, media, and interactive elements.
ContentCard and its sub-components render semantic HTML elements by default:
ContentCardrenders as<article>ContentCardHeaderrenders as<header>ContentCardFooterrenders as<footer>
You can override these defaults using the as prop on each component.
Basic Examples
ContentCard uses sub-components for flexible layout. Combine ContentCardHeader, ContentCardBody, and ContentCardFooter to create your card structure.
function Example() { return ( <VStack gap={2} maxWidth={375}> <ContentCard> <ContentCardHeader thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="CoinDesk" subtitle="News" actions={ <HStack gap={0}> <IconButton transparent accessibilityLabel="favorite" name="star" variant="secondary" /> <IconButton transparent accessibilityLabel="More options" name="more" variant="secondary" /> </HStack> } /> <ContentCardBody title="Ethereum Network Shatters Records With Hashrate Climbing to 464 EH/s" description="This is a description of the record-breaking hashrate milestone." label={ <HStack alignItems="flex-end" flexWrap="wrap" gap={0.5}> <Text as="p" color="fgMuted" font="label2" numberOfLines={1}> $3,081.01 </Text> <Text as="p" color="fgPositive" font="label2"> ↗ 6.37% </Text> </HStack> } /> <ContentCardFooter> <RemoteImageGroup shape="circle" size={32}> <RemoteImage src={assets.eth.imageUrl} /> <RemoteImage src={assets.btc.imageUrl} /> </RemoteImageGroup> <Button compact variant="secondary"> Share </Button> </ContentCardFooter> </ContentCard> </VStack> ); }
Media Placement
Use the mediaPlacement prop on ContentCardBody to control where media is positioned relative to the content.
function Example() { const exampleMedia = ( <RemoteImage alt="Ethereum background" source={ethBackground} width="100%" /> ); return ( <VStack gap={2} maxWidth={375}> <Text as="h3" font="headline"> mediaPlacement: top (default) </Text> <ContentCard> <ContentCardHeader thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="CoinDesk" subtitle="News" /> <ContentCardBody title="Media at top" description="The media appears above the text content." media={exampleMedia} mediaPlacement="top" /> </ContentCard> <Text as="h3" font="headline"> mediaPlacement: bottom </Text> <ContentCard> <ContentCardHeader thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="CoinDesk" subtitle="News" /> <ContentCardBody title="Media at bottom" description="The media appears below the text content." media={exampleMedia} mediaPlacement="bottom" /> </ContentCard> <Text as="h3" font="headline"> mediaPlacement: end </Text> <ContentCard> <ContentCardHeader thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="CoinDesk" subtitle="News" /> <ContentCardBody title="Media at end" description="The media appears to the right of the text content." media={exampleMedia} mediaPlacement="end" /> </ContentCard> <Text as="h3" font="headline"> mediaPlacement: start </Text> <ContentCard> <ContentCardHeader thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="CoinDesk" subtitle="News" /> <ContentCardBody title="Media at start" description="The media appears to the left of the text content." media={exampleMedia} mediaPlacement="start" /> </ContentCard> </VStack> ); }
With Background
Apply a background color to the card using the background prop. When using a background, consider using variant="tertiary" on buttons.
function Example() { const exampleMedia = ( <RemoteImage alt="Ethereum background" src={ethBackground} style={{ objectFit: 'cover', borderRadius: '24px' }} width="100%" /> ); return ( <VStack gap={2} maxWidth={375}> <ContentCard background="bgAlternate"> <ContentCardHeader thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="CoinDesk" subtitle="News" /> <ContentCardBody title="Card with Background" description="This card has an alternate background color." media={exampleMedia} /> <ContentCardFooter> <RemoteImageGroup shape="circle" size={32}> <RemoteImage src={assets.eth.imageUrl} /> <RemoteImage src={assets.btc.imageUrl} /> </RemoteImageGroup> <Button compact variant="tertiary"> Share </Button> </ContentCardFooter> </ContentCard> <ContentCard background="bgAlternate"> <ContentCardHeader thumbnail={<RemoteImage alt="Ethereum" shape="circle" size="l" source={ethBackground} />} title="CoinDesk" subtitle="News" /> <ContentCardBody title="No Media with Background" description="This card has no media element." /> <ContentCardFooter gap={4} justifyContent="space-between" paddingTop={0.5}> <IconCounterButton accessibilityLabel="like, 99 likes" count={99} icon="heart" /> <IconCounterButton accessibilityLabel="comment, 4200 comments" count={4200} icon="comment" /> <IconCounterButton accessibilityLabel="share, 32 shares" count={32} icon="arrowsHorizontal" /> </ContentCardFooter> </ContentCard> </VStack> ); }