Skip to main content

Image Management

The Ever Works Template includes an image domain management system that controls which external image hosts are allowed for Next.js Image optimization. The system maintains curated domain lists for common image providers and icon services, provides runtime domain management, URL validation, and generates remotePatterns configuration for next.config.js.

Architecture Overview

ComponentPathPurpose
image-domains.tslib/utils/image-domains.tsCore domain lists, pattern generation, and validation utilities
useImageDomainshooks/use-image-domains.tsReact hook for managing domains at runtime
useImageValidationhooks/use-image-domains.tsReact hook for validating image URLs against allowed domains

Pre-Configured Domains

The system ships with two curated domain lists:

Common Image Domains

These are standard image hosting services used for user avatars and content images:

DomainPurpose
lh3.googleusercontent.comGoogle user profile images
avatars.githubusercontent.comGitHub user avatars
platform-lookaside.fbsbx.comFacebook profile images
pbs.twimg.comTwitter/X profile images
images.unsplash.comUnsplash stock photography

Icon Domains

Dedicated icon and design asset services:

DomainPurpose
flaticon.comFlaticon icons
iconify.designIconify icon library
icons8.comIcons8 assets
feathericons.comFeather icon set
heroicons.comHeroicons library
tabler-icons.ioTabler icons

Next.js Remote Patterns

The generateImageRemotePatterns function creates the remotePatterns array for Next.js image configuration:

import { generateImageRemotePatterns } from '@/lib/utils/image-domains';

// next.config.js
module.exports = {
images: {
remotePatterns: generateImageRemotePatterns()
}
};

Generated Patterns

The function produces two types of patterns:

  1. Specific patterns with restricted pathnames for known services:
{
protocol: 'https',
hostname: 'lh3.googleusercontent.com',
pathname: '/a/**'
}
  1. Wildcard patterns for subdomains of all registered domains:
{
protocol: 'https',
hostname: '*.flaticon.com',
pathname: '/**'
}

Domain Validation

isAllowedImageDomain

Checks whether a URL's hostname is in the allowed domain list:

import { isAllowedImageDomain } from '@/lib/utils/image-domains';

isAllowedImageDomain('https://images.unsplash.com/photo-123') // true
isAllowedImageDomain('https://cdn.flaticon.com/icons/svg/123') // true (subdomain match)
isAllowedImageDomain('https://evil-site.com/image.jpg') // false
isAllowedImageDomain('/local/image.png') // true (non-HTTP URLs pass)

The function performs three levels of matching:

CheckDescription
Exact matchHostname matches a domain in either list exactly
Subdomain matchHostname ends with .{domain} for any registered domain
Non-HTTP passURLs without http:// or https:// prefix always pass

isValidImageUrl

Validates whether a string is a structurally valid image URL:

import { isValidImageUrl } from '@/lib/utils/image-domains';

isValidImageUrl('https://example.com/image.png') // true
isValidImageUrl('/local/image.png') // true (relative URLs allowed)
isValidImageUrl('') // false
isValidImageUrl('not-a-url') // false

isProblematicUrl

Detects URLs that are likely not direct image links:

import { isProblematicUrl } from '@/lib/utils/image-domains';

isProblematicUrl('https://flaticon.com/icone-gratuite/some-page') // true (page, not image)
isProblematicUrl('https://example.com?related_id=123') // true (redirect URL)
isProblematicUrl('https://example.com/photo.jpg') // false (valid image extension)
Detection RuleDescription
Flaticon page URLsURLs with /icone-gratuite/ path on flaticon.com
Redirect parametersURLs containing related_id= or origin= query parameters
Missing image extensionURLs without .jpg, .jpeg, .png, .gif, .webp, .svg, or .ico

shouldShowFallback

Determines whether to display a fallback icon instead of an image:

import { shouldShowFallback } from '@/lib/utils/image-domains';

shouldShowFallback('') // true (empty URL)
shouldShowFallback('https://flaticon.com/page/123') // true (problematic)
shouldShowFallback('https://example.com/icon.png') // false (valid image)

Runtime Domain Management

Adding Domains

import { addImageDomain } from '@/lib/utils/image-domains';

// Add as a common image domain
addImageDomain('cdn.example.com');

// Add as an icon domain
addImageDomain('my-icons.example.com', true);

The function is idempotent -- adding an already-registered domain has no effect.

Removing Domains

import { removeImageDomain } from '@/lib/utils/image-domains';

removeImageDomain('cdn.example.com');
// Removes from both COMMON_IMAGE_DOMAINS and ICON_DOMAINS

Getting All Domains

import { getAllowedDomains } from '@/lib/utils/image-domains';

const { common, icons } = getAllowedDomains();
// common: ['lh3.googleusercontent.com', 'avatars.githubusercontent.com', ...]
// icons: ['flaticon.com', 'iconify.design', ...]

Returns copies of the domain arrays, preventing external mutation.

The useImageDomains Hook

A React hook for managing image domains with state synchronization:

import { useImageDomains } from '@/hooks/use-image-domains';

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

return (
<div>
<h3>Common Domains ({domains.common.length})</h3>
<ul>
{domains.common.map(domain => (
<li key={domain}>
{domain}
<button onClick={() => removeDomain(domain)}>Remove</button>
</li>
))}
</ul>

<h3>Icon Domains ({domains.icons.length})</h3>
<ul>
{domains.icons.map(domain => (
<li key={domain}>{domain}</li>
))}
</ul>

<button onClick={() => addDomain('cdn.new-service.com')}>
Add Domain
</button>
</div>
);
}

Hook API

MethodParametersDescription
domains--Current state: { common: string[], icons: string[] }
addDomain(domain: string, isIconDomain?: boolean)Add a domain and refresh state
removeDomain(domain: string)Remove a domain (normalizes input) and refresh state
checkDomain(url: string)Check if a URL's domain is allowed

The removeDomain method normalizes the input by trimming whitespace, lowercasing, and stripping wildcard prefixes (*.).

The useImageValidation Hook

A lightweight hook for validating image URLs against the allowed domain list:

import { useImageValidation } from '@/hooks/use-image-domains';

function ImageUrlInput({ value, onChange }) {
const { checkImageUrl } = useImageValidation();

const handleChange = (url: string) => {
const { isValid, error } = checkImageUrl(url);
if (!isValid) {
console.warn(error);
// e.g., "Domain not allowed. Add cdn.example.com to image domains configuration."
}
onChange(url);
};

return <input value={value} onChange={(e) => handleChange(e.target.value)} />;
}

Validation Results

ScenarioisValiderror
Non-HTTP URL (relative path)true--
Allowed domaintrue--
Disallowed domainfalse"Domain not allowed. Add hostname to image domains configuration."
Invalid URL formatfalse"Invalid URL format"

Key Files

FilePath
Image Domains Utilitylib/utils/image-domains.ts
Image Domains Hookhooks/use-image-domains.ts
Image Validation Hookhooks/use-image-domains.ts