---
title: useCreateImageContent
description: >-
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`](/docs/api/shop-minis/commands/setup) CLI
command before using this hook so the content can be associated with the Mini.
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\\ CLI command before using this hook so the content can be associated with the Mini.
## useCreateImageContent()
### Returns
* **UseCreateImageContentReturns**
### ### UseCreateImageContentReturns
* **createImageContent**
**(params: CreateImageContentParams) => Promise<{ data: Content; userErrors?: ContentCreateUserErrors\[]; }>**
Upload an image and create content.
* **loading**
**boolean**
Whether the content is being created.
### 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
* #### Example code
##### Default
```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...
} > ) } ``` * #### Basic upload ##### Description A minimal example that uploads an image with a title and visibility. ##### Default ```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 ( ) } ``` * #### With an external ID ##### 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\`. ##### Default ```tsx import { useCreateImageContent, useImagePicker, ContentWrapper, Button, } 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}
} > ) } ``` * #### Shareable content with rich link previews ##### 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. ##### Default ```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 [For displaying images - ImageContentWrapper](../../components/primitives/imagecontentwrapper) [For sharing content via the native share sheet - useShare](../../hooks/util/useshare)