Skip to main content
Version: Latest

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();
},