Express and EJS Image Fallbacks for Server-Rendered Apps
Handle missing and broken images in Express.js EJS templates using route handlers, res.locals helpers, and fallback.pics placeholder URLs for empty media fields.
Express apps using EJS for server-rendered HTML can resolve image fallbacks at two layers: in route handlers before passing data to templates, and in EJS template helpers registered on res.locals that handle null or empty image fields.
Server-side resolution ensures that social crawlers, email clients, and first-paint users see correct images. Client-side onerror is a secondary safety net for CDN failures after the page loads.
Route handler
Resolve fallback URLs in Express route handlers
The route handler that queries the database is the right place to resolve null image fields. Add a fallback URL to the data object before passing it to res.render().
This keeps EJS templates free of conditional logic and ensures every downstream consumer of the data — email notifications, JSON API responses, scheduled jobs — gets the same resolved URL.
// routes/products.js
router.get('/products/:slug', async (req, res) => {
const product = await Product.findOne({ slug: req.params.slug });
if (!product) return res.status(404).render('404');
const imageUrl = product.imageUrl
?? `https://fallback.pics/api/v1/800x800?text=${encodeURIComponent(product.name)}`;
res.render('products/show', { product, imageUrl });
}); EJS template
Render fallback URLs in EJS templates
With the fallback URL resolved in the route handler, EJS templates simply reference the variable. Add an onerror attribute for files that pass the null check but return a CDN error.
EJS escapes HTML in <%= %> tags by default. That behavior protects against XSS in URL values without additional effort.
<%-- views/products/show.ejs --%>
<img
src="<%= imageUrl %>"
alt="<%= product.name %>"
width="800"
height="800"
onerror="this.onerror=null; this.src='https://fallback.pics/api/v1/800x800?text=Not+Found'"
loading="lazy"
/> Middleware helper
Register an imageUrl helper on res.locals
For fallback logic used across many routes, register a helper function on res.locals in middleware. EJS templates can call it directly, avoiding repetition in individual route handlers.
// middleware/templateHelpers.js
app.use((req, res, next) => {
res.locals.imageUrl = function(src, text, w = 400, h = 400) {
if (src) return src;
return `https://fallback.pics/api/v1/${w}x${h}?text=${encodeURIComponent(text)}`;
};
next();
});
// In EJS template
<img
src="<%= imageUrl(product.imageUrl, product.name, 800, 800) %>"
alt="<%= product.name %>"
width="800"
height="800"
/> User avatars
Avatar fallbacks with initials in Express
Register an avatarUrl helper in middleware that generates an initials-based avatar URL when the user has no profile photo.
This keeps avatar fallback logic in one place even when avatars appear in multiple templates — navigation headers, comment threads, and profile pages.
// In middleware
res.locals.avatarUrl = function(user) {
if (user.avatar) return user.avatar;
const initials = user.name
.split(' ')
.map(n => n[0])
.join('')
.toUpperCase()
.slice(0, 2);
return `https://fallback.pics/api/v1/avatar/64?text=${encodeURIComponent(initials)}`;
};
// In EJS template
<img
src="<%= avatarUrl(user) %>"
alt="<%= user.name %>"
width="64"
height="64"
onerror="this.onerror=null; this.src='https://fallback.pics/api/v1/avatar/64?text=??'"
/> OG image
Pass og:image to EJS layouts
For blog and product pages that need social sharing previews, pass an ogImage variable to the template and render it in the <head> partial.
Compute the fallback URL in the route handler with OG-appropriate dimensions. Append .jpg for social platform compatibility.
// Route handler
const ogImage = post.featuredImage
?? `https://fallback.pics/api/v1/1200x630.jpg?text=${encodeURIComponent(post.title)}`;
res.render('blog/post', { post, ogImage });
<%-- views/partials/head.ejs --%>
<meta property="og:image" content="<%= ogImage %>" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" /> Security
Escape user-controlled content in EJS templates
EJS escapes HTML in <%= %> tags by default. Use <%- %> only for pre-rendered HTML you control entirely. For URLs containing user-supplied text, use encodeURIComponent before embedding in the fallback URL.
Never put raw user input into an onerror JavaScript string. The fallback URL in the onerror attribute should be a hard-coded string without user-controlled segments.
Key takeaways
What to standardize before shipping
- Resolve null image fields in Express route handlers before passing data to res.render() so templates stay clean.
- Register an imageUrl helper on res.locals in middleware for fallback URL logic used across many routes.
- EJS escapes HTML in <%= %> by default; use encodeURIComponent for user-controlled text in fallback URL parameters.
- Set og:image in the route handler and pass it to the EJS layout for social sharing previews.
- Keep the onerror attribute value as a hard-coded fallback URL — never construct it from user-controlled input.
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.