Skip to main content

Media Hooks

The Ever Works Template provides hooks for managing media-related concerns: validating and managing allowed image domains, and extracting item details from URLs. These hooks ensure that images are loaded from trusted sources and that URL-based item submission works reliably.

Architecture Overview

Source Files

FilePurpose
hooks/use-image-domains.tsRuntime management and validation of allowed image domains
hooks/use-url-extraction.tsAI-powered extraction of item details from URLs
lib/utils/image-domains.tsUnderlying domain registry utilities

Image Domain Management

useImageDomains

Manages the list of allowed image domains at runtime. Next.js requires explicit domain configuration for remote images; this hook provides a dynamic layer on top of the static configuration.

function useImageDomains(): UseImageDomainsReturn

Return value:

FieldTypeDescription
domainsstring[]Current list of allowed domains
addDomain(domain: string, isIconDomain?: boolean) => voidAdd a domain to the allowed list
removeDomain(domain: string) => voidRemove a domain from the allowed list
checkDomain(url: string) => booleanCheck if a URL's domain is allowed

Domain normalization:

When removing a domain, the input is trimmed, lowercased, and stripped of wildcard prefixes (*.). This ensures consistent matching regardless of how the domain was originally added.

Usage example:

const { domains, addDomain, removeDomain, checkDomain } = useImageDomains();

// Add a new domain for user-submitted images
addDomain('cdn.example.com');

// Add an icon-specific domain
addDomain('icons.example.com', true);

// Check before rendering
if (checkDomain(imageUrl)) {
// Safe to render with next/image
}

useImageValidation

A focused hook for validating individual image URLs against the allowed domain list. Returns a structured result with an error message when validation fails.

function useImageValidation(): { checkImageUrl: (url: string) => ValidationResult }

Validation rules:

InputResult
Relative path (no protocol){ isValid: true }
HTTP/HTTPS URL with allowed domain{ isValid: true }
HTTP/HTTPS URL with disallowed domain{ isValid: false, error: "Domain not allowed..." }
Malformed URL{ isValid: false, error: "Invalid URL format" }

Relative paths are always considered valid because they reference local assets that do not require domain allowlisting.

Usage example:

const { checkImageUrl } = useImageValidation();

const result = checkImageUrl('https://cdn.example.com/photo.jpg');
if (!result.isValid) {
toast.error(result.error);
}

URL Extraction

useUrlExtraction

Extracts structured item data from a URL using the platform's extraction API. The extraction endpoint uses AI or scraping to pull out a name, description, category, tags, brand information, and images from the target page.

function useUrlExtraction(): UseUrlExtractionReturn

Return value:

FieldTypeDescription
isLoadingbooleanWhether an extraction is in progress
extractFromUrl(url: string, existingCategories?: string[]) => Promise<ExtractionResult | null>Trigger extraction

ExtractionResult fields:

FieldTypeDescription
namestringExtracted item name
descriptionstringExtracted item description
categorystringSuggested category
tagsstring[]Suggested tags
brandstringBrand name if detected
brand_logo_urlstringBrand logo URL if found
imagesstring[]Images found on the page

API endpoint: POST /api/extract

The request body includes the URL and optionally an array of existing categories. Providing existing categories helps the extraction API suggest a matching category rather than inventing a new one.

Graceful degradation:

If the extraction feature is disabled on the server (the response contains featureDisabled: true), the hook returns null silently without showing an error. This allows the UI to function normally even when AI extraction is not available.

Error handling:

On failure, the hook logs the error and shows a toast notification via sonner. The extractFromUrl function returns null on error, so callers can safely check the return value.

Usage example:

const { isLoading, extractFromUrl } = useUrlExtraction();

const handlePaste = async (url: string) => {
const result = await extractFromUrl(url, existingCategories);
if (result) {
form.setValue('name', result.name);
form.setValue('description', result.description);
if (result.tags) form.setValue('tags', result.tags);
if (result.category) form.setValue('category', result.category);
}
};

Integration with Item Submission

The media hooks work together in the item submission flow:

When a user pastes a URL during item submission:

  1. useUrlExtraction calls the server API to extract structured data.
  2. The form is pre-populated with the extracted name, description, tags, and category.
  3. useImageValidation checks each extracted image URL against the allowed domains.
  4. If an image domain is not allowed, the admin can use useImageDomains to add it.

Configuration

Image domains are configured at two levels:

LevelLocationDescription
Staticnext.config.js images.remotePatternsBuild-time domain configuration
Dynamiclib/utils/image-domains.tsRuntime domain additions via hooks

The runtime registry supplements the static configuration. Domains added at runtime are available immediately without requiring a rebuild.

Further Reading