Skip to main content

Building a Shoppable Videos mini

What will you learn?

In this guide, you'll learn how to do the following:

  • Accept data from an entry point.
  • Render a list of videos.
  • Show products connected to each video.

Prerequisites

This guide assumes that you have your test store set up and are able to create entry points but mock data will be provided if you are just getting started. You could alternatively fetch this data from your own API if you wish.

Step 1: Set up your entry point

The first thing to do is to create an entry point with a list of videos and associated products. For this example I will be using the data that comes from the STORE_PAGE VIDEO_COLLECTION_V2 entry point.

For development purpose, we have included mocked data which populates the mini with a few videos and associated products. We advise you to use this mock data to get started and then switch to using the real entry points data once you have set up your entry point(s).

You can create an entry point for this guide like so:

mutation SetEntryPoint {
entryPointSetByOwner(
shopDomain: "shop-minis-test-store.myshopify.com",
location: PRODUCT_PAGE,
ownerId: "gid://shopify/Product/7541934391551",
input: {
videoCollectionV2:{
items:[
{
video: {
embedUrl: "https://stream.mux.com/tQe00IYarTC202VrQIpjngZzhuE8goZ14D.m3u8"
},
fallbackImage: {
url: "https://cdn.shopify.com/s/files/1/0621/0463/3599/products/mediamodifier-kJXGTOY1wLQ-unsplash.jpg?v=1651847844"
},
contentCreator:{
avatar:{
url: "https://cdn.shopify.com/s/files/1/0621/0463/3599/products/vin-stratton-V1dX9O1B4pM-unsplash_1946x.jpg?v=1651847754",
},
name: "Yu Tuba"
},
relatedProducts: [
{productId: "gid://shopify/Product/7922587762943"},
{productId: "gid://shopify/Product/7541927510271"},
{productId: "gid://shopify/Product/7541934391551"},
],
externalId: "1"
},
# ... more items
]
}
}
) {
entryPoint {
id
ownerId
visibilityRule
}
}
}

Step 2: Reading entry point data

In your mini you can now use the useMinisParams() hook to get hold of entryPoint.collectionItems. When a mini is launched from a VIDEO_COLLECTION_V2 or IMAGE_COLLECTION_V2 entry point this object will contain all of the entry point items. We can use that for our first page of data instead of fetching it from an API which will help our mini first load to feel fast and responsive. Subsequent pages of data will need to be fetched from elsewhere and it's result should return in the fetchMore property.

import {
ShoppableVideos,
VideoCollectionV2EntryPointItem,
useMinisParams,
} from '@shopify/shop-minis-platform-sdk'

export const ShoppableVideosScreen = () => {
const {entryPoint, entryPointParams} = useMinisParams()
const [listVideos, setListVideos] = useState<
VideoCollectionV2EntryPointItem[]
>([])

useEffect(() => {
// This is where you would get videos from the entry point for the first load
setListVideos(entryPoint.collectionItems)
}, [entryPoint?.collectionItems])

const shoppableVideos = useMemo(
// This is a helper function that maps the entry point items to the ShoppableVideosItem type
() => mapEntryPointCollectionItemsToShoppableVideos(listVideos),
[listVideos]
)

const fetchMore = async () => {
const moreVideos = (await Promise.resolve(
// Fetch more videos from your API
)) as VideoCollectionV2EntryPointItem[]

setListVideos(list => list.concat(moreVideos))
}

return (
<ShoppableVideos
items={shoppableVideos}
shopId={entryPointParams.shopGID}
fetchMore={fetchMore}
/>
)
}

function mapEntryPointCollectionItemsToShoppableVideos(
collectionItems: VideoCollectionV2EntryPointItem[]
) {
return collectionItems.map(item => ({
externalId: item.externalId!,
videoUrl: item.video.embeddedUrl!,
fallbackImageUrl: item.fallbackImage.url,
relatedProductsIds: item.relatedProducts.map(
relatedProduct => relatedProduct.productId
),
}))
}

As mentioned above mock data is provided if you cannot use entry point data yet. Just import entryPoint and entryPointParams from mockData.ts instead of using useMinisParams(). This mock data assumes you are using enableApiSandbox so toggle that back on if you previously disabled it.

Step 3: Rendering a list of videos

The result you'll get with this data is a list of videos that look like this:

Feed screen of a Shoppable Videos Mini

Step 4: Customizing your Shopable Videos mini

You can customize the look and feel of your Shoppable Videos mini by using the ShoppableVideos component's props. You can find the full list of props here: ShoppableVideosProps.

Example

// ...
<ShoppableVideos
items={shoppableVideos}
shopId={entryPointParams.shopGID}
fetchMore={fetchMore}
renderItem={({item}) => (
<View>
<CustomVideoHeader />
<Video
source={{uri: item.videoUrl}}
style={{height, width: 'auto'}}
poster={item.fallbackImageUrl}
posterResizeMode="cover"
repeat
resizeMode="cover"
paused={false}
progressUpdateInterval={10}
muted
ignoreSilentSwitch="ignore"
testID="video"
/>
<CustomVideoFooter />
</View>
)}
/>
// ...

Feed screen of a Shoppable Videos Mini with custom UI

// ...
<ShoppableVideos
items={shoppableVideos}
shopId={entryPointParams.shopGID}
fetchMore={fetchMore}
renderMedia={({item}) => <CustomCarouselPost images={imageStack(item)} />}
/>
// ...

Feed screen of a Shoppable Videos Mini with custom UI

// ...
<ShoppableVideos
items={shoppableVideos}
shopId={entryPointParams.shopGID}
fetchMore={fetchMore}
renderMediaHeader={() => null}
/>
// ...

Feed screen of a Shoppable Videos Mini with custom UI

// ...
<ShoppableVideos
items={shoppableVideos}
shopId={entryPointParams.shopGID}
fetchMore={fetchMore}
renderMediaFooter={({item}) => (
<View>
<Pressable
onPress={() => setShowGift(!showGift)}
style={{
justifyContent: 'flex-end',
marginBottom: 20,
}}
>
<IconButton
name="gift"
onPress={() => setShowGift(!showGift)}
accessibilityLabel="products"
/>
</Pressable>
<RelatedProductsList
products={item.relatedProducts}
style={RELATED_PRODUCTS_LIST_STYLE}
shopId={entryPointParams.shopGID}
/>
{showGift ? (
<GiftBottomSheet
text='Use PROMO2023 to get 10% off your purchase'
/>
) : null}
</View>
)}
/>
// ...

Feed screen of a Shoppable Videos Mini with custom UIFeed screen of a Shoppable Videos Mini with custom UI

We look forward to seeing what you build!