Skip to main content

Building a Shoppable Posts 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 posts.
  • Show products connected to each post.

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 posts and associated products. For this example I will be using the data that comes from the STORE_PAGE IMAGE_COLLECTION_V2 entry point.

For development purpose, we have included mocked data which populates the mini with a few posts 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: {
imageCollectionV2:{
items:[
{
image: {
url: "https://cdn.shopify.com/s/files/1/0621/0463/3599/products/vin-stratton-V1dX9O1B4pM-unsplash_990x.jpg?v=1651847754"
},
relatedProducts: [
{productId: "gid://shopify/Product/7922587762943"},
{productId: "gid://shopify/Product/7541927510271"},
{productId: "gid://shopify/Product/7541934391551"},
],
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
displayDate: "2022-08-15T13:30:00Z",
externalId: "1",
},
# ... other 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 fetchMoreByProductIds or fetchMoreByShopId property. Check out the ShoppablePosts template properties for more information.

import {useMinisParams, ShoppablePosts, ImageCollectionV2EntryPointItem} from '@shopify/shop-minis-platform-sdk'

export const ShoppablePostsScreen = () => {
const {entryPoint} = useMinisParams()

const shoppablePosts = useMemo(
// This is a helper function that maps the entry point items to the ShoppablePostsItem type
() => mapEntryPointCollectionItemsToShoppablePosts(entryPoint.collectionItems),
[entryPoint.collectionItems]
)

const fetchMoreByProductIds = useCallback((productIds: string[]) => {
// Fetch more posts from your API based on the productIds
return fetchMorePostsFromServer(productIds)
}, [])

const fetchMoreByShopId = useCallback((shopId: string) => {
// Fetch more posts from your API by shopId
return fetchMorePostsFromServer(shopId)

}, [])

return (
<ShoppablePosts
items={shoppablePosts}
shopId={entryPointParams.shopGID}
fetchMoreByProductIds={fetchMoreByProductIds}
fetchMoreByShopId={fetchMoreByShopId}
/>
)
}

function mapEntryPointCollectionItemsToShoppablePosts(
collectionItems: ImageCollectionV2EntryPointItem[]
) {
return collectionItems.map(item => ({
externalId: item.externalId!,
description: item.description!,
displayDate: item.displayDate!,
image: {url: item.image.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 posts

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

Feed screen of a Shoppable Posts Mini

Step 4: Customizing your Shopable Posts mini

You can customize the look and feel of your Shoppable Posts mini by using the ShoppablePosts component's props. You can find the full list of props here: ShoppablePostsCustomisationProps.

Example

// ...
<ShoppablePosts
items={shoppablePosts}
shopId={entryPointParams.shopGID}
fetchMoreByShopId={fetchMoreByShopId}
renderMedia={({item}) => <CustomCarouselPost images={[imageStack(item.image.url)]} />}
/>
// ...

Feed screen of a Shoppable Posts Mini with custom UI

// ...
<ShoppablePosts
items={shoppablePosts}
shopId={entryPointParams.shopGID}
fetchMoreByShopId={fetchMoreByShopId}
renderItem={({item}) => (
<View>
<Image
source={{uri: item.image.url}}
style={{width: '100%', height: 500}}
resizeMode="cover"
/>
<Pressable
onPress={() => setShouldShow(!shouldShow)}
style={{
height: 20,
width: 20,
borderRadius: 20,
position: 'absolute',
backgroundColor: '#c3c3c3',
borderWidth: 1,
borderColor: '#ffffff',
top: 280,
left: 100,
}}
>
{shouldShow ? (
<View
style={{
position: 'absolute',
top: 10,
left: 20,
width: 200,
height: 200,
}}
>
<ProductLink
product={FIXTURE_PRODUCT}
shopId={entryPointParams.shopGID}
/>
</View>
) : null}
</Pressable>
</View>
)}
/>
// ...

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

// ...
<ShoppablePosts
items={shoppablePosts}
shopId={entryPointParams.shopGID}
fetchMoreByShopId={fetchMoreByShopId}
renderHeader={() => null}
/>
// ...

Feed screen of a Shoppable Posts Mini with custom UI

// ...
<ShoppablePosts
items={shoppablePosts}
shopId={entryPointParams.shopGID}
fetchMoreByShopId={fetchMoreByShopId}
renderMediaFooter={({item}) => (
<Box flexDirection="column" margin="gutter">
<Text variant="bodySmall">{item.description}</Text>
<DisplayDate date={item.displayDate} />
</Box>
)}
/>
// ...

Feed screen of a Shoppable Posts Mini with custom UI

We look forward to seeing what you build!