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
| Property | Type | Description |
|---|---|---|
query | string | The user's search query. |
page | number | Page number for pagination (starts at 1). |
perPage | number | Results per page (default 20). |
orientation | 'landscape' | 'portrait' | 'square' | undefined | Optional orientation filter. |
color | string | undefined | Optional 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();
},