--- title: useCreateImageContent description: >- The useCreateImageContent hook combines image upload with content creation to generate user-generated content entries. api_name: shop-minis source_url: html: 'https://shopify.dev/docs/api/shop-minis/hooks/content/usecreateimagecontent' md: >- https://shopify.dev/docs/api/shop-minis/hooks/content/usecreateimagecontent.md --- # useCreateImageContent The `useCreateImageContent` hook combines image upload with content creation to generate user-generated content entries. Built on top of `useImageUpload`, it extends the basic upload functionality with content management features including titles, descriptions, visibility controls, product associations, and unique IDs for sharing and discovery. ### Parameters The `createImageContent` function accepts an object with the following fields: * **`image`** (required): A `File` object representing the image to upload. * **`contentTitle`** (required): The title for the content entry. * **`visibility`** (optional): An array of `ContentVisibility` values (see below). * **`externalId`** (optional): A unique identifier from your own system that lets you look up content later via `ContentWrapper` using an ID you already know. If not provided, the content can only be retrieved by its `publicId`. Each `externalId` must be unique per Mini — creating content with a duplicate `externalId` returns a `DUPLICATE_EXTERNAL_ID` error. * **`description`** (optional): A text description for the content. Displayed alongside the content in Shop surfaces such as feeds and content detail views. Use this to provide context about the image, such as a caption or review text. * **`productIds`** (optional): An array of Shopify product GIDs (e.g. `'gid://shopify/Product/123'`) to associate with the content. Maximum 20 products. Associated products appear alongside the content, enabling shoppable content experiences. If any product IDs are ineligible, the mutation will return an `INELIGIBLE_PRODUCTS` error. ### Visibility Options The `visibility` parameter accepts an optional array of `ContentVisibility` values: * **`DISCOVERABLE`**: Makes content eligible for Shop's recommendation and discovery systems. Your content can appear in feeds and recommendations across the Shop app. * **`LINKABLE`**: Enables shareable URLs for the content. When set, the created `Content` object includes a `shareableUrl` field containing a URL that can be shared externally. The shareable page renders OpenGraph meta tags (`og:title`, `og:image`, `og:description`) using the content's `contentTitle`, uploaded image, and `description` — so link previews in social media, messaging apps, and other platforms will display a rich card with the content's title, image, and description. Use the `useShare` hook to trigger the native share sheet with this URL. ### Error Handling The hook may return `userErrors` in the response with the following codes: * **`DUPLICATE_EXTERNAL_ID`**: Returned when an `externalId` is provided that already exists for this Mini. Each `externalId` must be unique per content entry. * **`INELIGIBLE_PRODUCTS`**: Returned when one or more `productIds` refer to products that are not eligible for Shop. The error message includes the specific ineligible product GIDs. ### Use Cases * **Shareable content with rich link previews**: Include `LINKABLE` in the `visibility` array and provide a `contentTitle`, `description`, and image. The returned `shareableUrl` points to a page with OpenGraph tags, so when shared via social media or messaging apps the link preview displays the content's title, image, and description as a rich card. Pass the `shareableUrl` to the `useShare` hook to open the native share sheet. * **User reviews with photos**: Provide `description` for the review text, `productIds` to link the reviewed products, and `['DISCOVERABLE', 'LINKABLE']` visibility for maximum reach. * **Shoppable lookbooks**: Upload styled images with associated `productIds` so users can shop the look. * **Content lookup by your own ID**: Set `externalId` to an identifier from your system (e.g. a database row ID or slug) so you can retrieve the content later via `ContentWrapper` without storing the `publicId`. The hook handles both the image upload pipeline and content entity creation in a single operation, automatically associating content with your Mini and integrating with Shop's content systems. **Caution:** You must run the [`setup`](https://shopify.dev/docs/api/shop-minis/commands/setup) CLI command before using this hook so the content can be associated with the Mini. ## useCreateImageContent() ### Returns * **UseCreateImageContentReturns** ### UseCreateImageContentReturns * createImageContent Upload an image and create content. ```ts (params: CreateImageContentParams) => Promise<{ data: Content; userErrors?: ContentCreateUserErrors[]; }> ``` * loading Whether the content is being created. ```ts boolean ``` ### CreateImageContentParams * contentTitle The title for the content entry. ```ts string ``` * description A text description for the content. Displayed alongside the image in Shop surfaces such as feeds and content detail views. Use this to provide context about the image, such as a caption or review text. ```ts string ``` * externalId A unique identifier from your own system that lets you look up content later via \`ContentWrapper\` using an ID you already know. If not provided, the content can only be retrieved by its \`publicId\`. Each \`externalId\` must be unique per Mini — creating content with a duplicate returns a \`DUPLICATE\_EXTERNAL\_ID\` error. ```ts string ``` * image The image file to upload. ```ts File ``` * productIds An array of Shopify product GIDs (e.g. \`'gid://shopify/Product/123'\`) to associate with the content. Maximum 20 products. Associated products appear alongside the content, enabling shoppable content experiences. ```ts string[] ``` * visibility Visibility options for the content. Use \`\['DISCOVERABLE']\` to appear in recommendations, \`\['LINKABLE']\` to enable shareable URLs, or both. Pass \`null\` or \`\[]\` to keep content private within your Mini. ```ts ContentVisibility[] | null ``` ### ContentVisibility ```ts 'DISCOVERABLE' | 'LINKABLE' ``` ### Content * description ```ts string | null ``` * externalId ```ts string | null ``` * image ```ts ContentImage ``` * products ```ts ContentProduct[] | null ``` * publicId ```ts string ``` * shareableUrl ```ts string | null ``` * status ```ts MinisContentStatus | null ``` * title ```ts string ``` * visibility ```ts ContentVisibility[] ``` ### ContentImage * altText ```ts string | null ``` * height ```ts number | null ``` * id ```ts string | null ``` * thumbhash ```ts string | null ``` * url ```ts string ``` * width ```ts number | null ``` ### ContentProduct * featuredImage ```ts ContentImage | null ``` * id ```ts string ``` * title ```ts string ``` ### MinisContentStatus * PENDING ```ts PENDING ``` * READY ```ts READY ``` * REJECTED ```ts REJECTED ``` ### ContentCreateUserErrors * code ```ts ContentCreateUserErrorCode ``` * message ```ts string ``` ### ContentCreateUserErrorCode * DUPLICATE\_EXTERNAL\_ID ```ts DUPLICATE_EXTERNAL_ID ``` * INELIGIBLE\_PRODUCTS ```ts INELIGIBLE_PRODUCTS ``` Examples ### Examples * #### ##### tsx ```tsx import { useCreateImageContent, useImagePicker, Button, } from '@shopify/shop-minis-react' export default function MyComponent() { const {createImageContent, loading} = useCreateImageContent() const {openCamera, openGallery} = useImagePicker() const handleCameraCapture = async () => { try { const file = await openCamera() const result = await createImageContent({ image: file, contentTitle: 'Photo from camera', visibility: ['DISCOVERABLE', 'LINKABLE'], // Optional: set an external ID for deduplication and lookup externalId: 'my-unique-id-123', // Optional: add a description description: 'A photo captured with the camera', // Optional: associate up to 20 products (by GID) productIds: ['gid://shopify/Product/1'], }) console.log({data: result.data, userErrors: result.userErrors}) } catch (error) { console.error('Failed to capture and upload image:', error) } } const handleGallerySelect = async () => { try { const file = await openGallery() const result = await createImageContent({ image: file, contentTitle: 'Photo from gallery', // Visibility options: // - ['DISCOVERABLE'] - Appears in Shop recommendations // - ['LINKABLE'] - Enables shareable URLs // - ['DISCOVERABLE', 'LINKABLE'] - Both features // - null or [] - Private within Mini only visibility: ['DISCOVERABLE', 'LINKABLE'], description: 'A photo selected from the gallery', }) console.log({data: result.data, userErrors: result.userErrors}) } catch (error) { console.error('Failed to select and upload image:', error) } } return ( <> {loading &&
Uploading image...
} > ) } ``` * #### ##### Description A minimal example that uploads an image with a title and visibility. ##### tsx ```tsx import { useCreateImageContent, useImagePicker, Button, } from '@shopify/shop-minis-react' export default function BasicUpload() { const {createImageContent, loading} = useCreateImageContent() const {openGallery} = useImagePicker() const handleUpload = async () => { try { const file = await openGallery() const result = await createImageContent({ image: file, contentTitle: 'My photo', visibility: ['DISCOVERABLE', 'LINKABLE'], }) console.log('Created content:', result.data.publicId) } catch (error) { console.error('Upload failed:', error) } } return ( ) } ``` * #### ##### Description Set \`externalId\` to an identifier from your own system so you can look up the content later via \`ContentWrapper\` without needing to store the \`publicId\`. ##### tsx ```tsx import { useCreateImageContent, useImagePicker, ContentWrapper, Button, Image, } from '@shopify/shop-minis-react' /** * Use `externalId` to look up content later using an ID you already know, * without needing to store the `publicId`. * * For example, if your Mini lets users upload a profile photo, you can set * the `externalId` to the user's ID in your system and later retrieve * the content with `Loading...
if (!content) return null return{error}
} > ) } ``` * #### ##### Description Create content with \`LINKABLE\` visibility to get a \`shareableUrl\`. The URL renders OpenGraph meta tags (\`og:title\`, \`og:image\`, \`og:description\`) from the content's title, image, and description, so link previews in social media and messaging apps display a rich card. Use \`useShare\` to trigger the native share sheet. ##### tsx ```tsx import {useState} from 'react' import { useCreateImageContent, useImagePicker, useShare, Button, } from '@shopify/shop-minis-react' /** * Create shareable content with rich link previews. * * When `LINKABLE` is included in the `visibility` array, the created content * includes a `shareableUrl`. This URL points to a page that renders OpenGraph * meta tags (`og:title`, `og:image`, `og:description`) from the content's * title, image, and description — so link previews in social media, messaging * apps, and other platforms display a rich card automatically. */ export default function ShareableContent() { const {createImageContent, loading} = useCreateImageContent() const {openGallery} = useImagePicker() const {share} = useShare() const [shareableUrl, setShareableUrl] = useStateShareable link: {shareableUrl}
)} > ) } ``` *** ## Related [- ImageContentWrapper](https://shopify.dev/docs/api/shop-minis/components/primitives/imagecontentwrapper) [- useShare](https://shopify.dev/docs/api/shop-minis/hooks/util/useshare) ***