Skip to main content

AI Image Search

Search stock photo libraries from within the editor. Users type a query, apply filters, and insert results directly into their design.

Configuration

ai: {
mode: 'external',
features: {
imageSearch: true,
},
callbacks: {
onImageSearch: async (params: ImageSearchParams) => {
// query your stock photo provider
return result;
},
},
}

ImageSearchParams

PropertyTypeDescription
querystringThe user's search query.
pagenumberPage number for pagination (starts at 1).
perPagenumberResults per page (default 20).
orientation'landscape' | 'portrait' | 'square' | undefinedOptional orientation filter.
colorstring | undefinedOptional dominant color filter (hex string, e.g. '#ff0000').

ImageSearchResult

interface ImageSearchResult {
images: Array<{
id: string; // unique identifier from your provider
url: string; // full-resolution URL
thumbnailUrl: string; // preview thumbnail URL
width: number;
height: number;
author?: string; // photographer attribution
authorUrl?: string; // link to photographer profile
source?: string; // provider name (e.g. 'Unsplash')
}>;
totalResults: number; // total matching results (for pagination)
hasMore: boolean; // whether more pages are available
}

Example: Unsplash Integration

onImageSearch: async (params: ImageSearchParams): Promise<ImageSearchResult> => {
const url = new URL('https://api.unsplash.com/search/photos');
url.searchParams.set('query', params.query);
url.searchParams.set('page', String(params.page));
url.searchParams.set('per_page', String(params.perPage));
if (params.orientation) {
url.searchParams.set('orientation', params.orientation);
}
if (params.color) {
url.searchParams.set('color', params.color.replace('#', ''));
}

const response = await fetch(url.toString(), {
headers: { Authorization: `Client-ID ${UNSPLASH_ACCESS_KEY}` },
});
const data = await response.json();

return {
images: data.results.map((photo: any) => ({
id: photo.id,
url: photo.urls.regular,
thumbnailUrl: photo.urls.thumb,
width: photo.width,
height: photo.height,
author: photo.user.name,
authorUrl: photo.user.links.html,
source: 'Unsplash',
})),
totalResults: data.total,
hasMore: params.page * params.perPage < data.total,
};
},
tip

The author and source fields are displayed as attribution below each image in the search results grid. Include them to comply with stock photo licensing.

Pagination

The editor calls onImageSearch again with an incremented page when the user scrolls to the bottom of the results. Return hasMore: false to stop pagination.

Error Handling

If the callback throws, the editor displays an error message in the search panel. The user can retry by clicking the search button again.

onImageSearch: async (params) => {
const res = await fetch(`/api/images/search?q=${encodeURIComponent(params.query)}`);
if (!res.ok) throw new Error('Search failed');
return await res.json();
},