Restaurant Menu and Dish Photo Fallbacks
Keep restaurant menus and food delivery apps polished with restaurant menu placeholder images that hold layout when dish photos are missing or fail to load.
Restaurant menus and food delivery apps live or die on photography. A broken dish photo—especially in the hero slot or a featured item card—reads as low-quality to the customer even when the food is excellent. Restaurant menu placeholder images bridge the gap between a SKU being added to the system and the actual photo arriving from the photography team.
The fallback.pics API generates warm-toned, correctly sized placeholder images from a URL with no upload required. Point your img onerror at the right endpoint and every menu item renders with a consistent visual regardless of photo status.
Why it matters
Missing dish photos increase abandonment on food delivery apps
Studies of food delivery UX consistently show that menu items without photos receive significantly fewer clicks. The pattern is intuitive: diners use photos to decide. A broken image icon or a collapsed layout slot is worse than a neutral placeholder because it signals neglect.
The problem is structural. New menu items go live before the photography workflow completes. Specials change daily. Seasonal items rotate. Even well-staffed operations have windows where photos are absent. A reliable restaurant menu placeholder image closes that window without blocking the menu publish.
The secondary issue is layout shift. When an img element has no src fallback, browsers collapse the element to zero height. That collapses the card, reflows the grid, and produces a cumulative layout shift score that Google measures. A dimensioned fallback prevents all of that.
onerror pattern
Wire the fallback into your img tag with onerror
The simplest integration is an inline onerror attribute that swaps in the placeholder URL when the real photo fails to load. This works in any HTML context—server-rendered templates, React JSX, Vue templates, or plain HTML files.
One important detail: set the onerror to clear itself before assigning the fallback src. If the fallback URL itself ever fails (unlikely for a CDN-hosted service, but worth guarding), without clearing onerror you get an infinite loop of error events trying to load the same broken URL.
<!-- Inline onerror with loop guard -->
<img
src="{{ dish.photo_url }}"
width="400"
height="300"
alt="{{ dish.name }}"
onerror="this.onerror=null;this.src='https://fallback.pics/api/v1/400x300/F97316/FFFFFF?text=No+Photo'"
/>
<!-- React equivalent -->
<img
src={dish.photoUrl}
width={400}
height={300}
alt={dish.name}
onError={(e) => {
e.currentTarget.onerror = null;
e.currentTarget.src =
'https://fallback.pics/api/v1/400x300/F97316/FFFFFF?text=No+Photo';
}}
/> Category colors
Assign placeholder colors by food category
A flat gray placeholder for every missing dish photo is functional but uninspired. A small mapping table lets you assign warm oranges to mains, greens to salads, yellows to desserts, and blues to drinks. The color communicates category even before the real photo loads.
This approach also makes the 'missing photo' state feel intentional rather than accidental. Customers perceive it as a design choice, not a bug.
const CATEGORY_COLORS: Record<string, { bg: string; fg: string }> = {
mains: { bg: 'F97316', fg: 'FFFFFF' },
salads: { bg: '10B981', fg: 'FFFFFF' },
desserts: { bg: 'FBBF24', fg: '1F2937' },
drinks: { bg: '3B82F6', fg: 'FFFFFF' },
default: { bg: '7C3AED', fg: 'FFFFFF' },
};
function dishFallbackUrl(category: string, name: string, w = 400, h = 300) {
const { bg, fg } = CATEGORY_COLORS[category] ?? CATEGORY_COLORS.default;
const text = encodeURIComponent(name.slice(0, 20));
return `https://fallback.pics/api/v1/${w}x${h}/${bg}/${fg}?text=${text}`;
} Delivery app pattern
Handle CDN images with unknown failure modes
Food delivery platforms source dish photos from restaurant operators, third-party photography services, and sometimes AI-generated assets. Each source has its own failure mode: the restaurant deletes the photo from their system, the CDN key expires, the image CDN returns a 404 for a renamed path. None of those failures are under your control.
A universal fallback at the component level means none of those upstream failures ever produce a broken image icon. Wrap the img in a component, centralize the fallback URL construction, and never rely on individual restaurant systems being reliable.
// DishPhoto.tsx
interface DishPhotoProps {
src?: string;
name: string;
category: string;
width?: number;
height?: number;
}
export function DishPhoto({
src,
name,
category,
width = 400,
height = 300,
}: DishPhotoProps) {
const fallback = dishFallbackUrl(category, name, width, height);
return (
<img
src={src ?? fallback}
width={width}
height={height}
alt={name}
loading="lazy"
onError={(e) => {
e.currentTarget.onerror = null;
e.currentTarget.src = fallback;
}}
/>
);
} Animated option
Use skeleton placeholders while photos load asynchronously
If your app fetches dish photo URLs from an API rather than embedding them in the initial HTML, there is a window where the component renders but the URL is not yet known. A skeleton placeholder is a better UX than a blank box during that window.
The animated skeleton route produces a shimmer-effect SVG at any dimension. Show it while the data fetch is in-flight, then swap in the real photo or the static fallback based on what the API returns.
<!-- Animated skeleton while data loads -->
<img
src="https://fallback.pics/api/v1/animated/skeleton/400x300"
width="400"
height="300"
alt="Loading dish photo"
/> Internal links
More on image fallback patterns
The patterns in this post apply to any image-heavy ecommerce context. The fallback.pics documentation covers the full API surface including color parameters, text truncation, and format options.
https://fallback.pics/docs/
https://fallback.pics/placeholder-image-api/
https://fallback.pics/blog/food-menu-image-fallbacks/
https://fallback.pics/blog/cart-thumbnail-image-fallback/ Key takeaways
What to standardize before shipping
- Missing dish photos cause measurable abandonment; a styled placeholder is always better than a broken icon.
- Set explicit width and height on every img element to prevent layout shift regardless of photo load state.
- Clear onerror before assigning the fallback src to prevent infinite error loops.
- Map food categories to placeholder colors so missing photos still communicate visual category.
- Use animated skeleton placeholders during async data fetches, then swap to static fallbacks or real photos.
Production fallback layer
Use fallback.pics anywhere an image URL is accepted.
Start with one deterministic URL and standardize fallback behavior across your design system.