Animated Skeleton Placeholders via URL (No CSS Required)
Generate animated skeleton loading placeholders from a URL alone. No CSS keyframes, no JS shimmer libraries — just an img src pointing to fallback.pics.
Skeleton loaders reduce perceived wait time by giving users an immediate structural preview of the content layout. Building them with CSS keyframes and component wrappers is fine for design systems, but for simpler cases a URL-generated animated SVG lets you drop a skeleton into any img tag with zero extra code.
The fallback.pics animated skeleton route embeds a CSS animation inside an SVG, producing a shimmer effect that works in every browser without JavaScript or stylesheet dependencies. The output is deterministic and CDN-cacheable like every other fallback.pics URL.
How it works
Animated SVG without external CSS
SVG supports inline style and CSS animation natively. A skeleton SVG can contain a linearGradient that shifts position using a CSS keyframe declared inside a style element within the SVG document. When the browser renders the SVG as an img src, the animation runs independently of the page stylesheet.
The important constraint is that SVGs loaded via img src are sandboxed from the page. They cannot read external stylesheets or run JavaScript. All animation logic must live inside the SVG itself. fallback.pics handles that packaging — you get a self-contained animated SVG from a plain URL.
Basic URL
Drop a skeleton placeholder into any img tag
Use the /animated/skeleton/ prefix followed by dimensions. The default output is a light-gray skeleton with a left-to-right shimmer in the style of a card loading state.
Specify width and height attributes on the img element to match the final content dimensions. That prevents layout shift when the real image loads.
<!-- Standard card skeleton -->
<img
src="https://fallback.pics/api/v1/animated/skeleton/600x400"
width="600"
height="400"
alt="Loading image"
/>
<!-- Square avatar skeleton -->
<img
src="https://fallback.pics/api/v1/animated/skeleton/80x80"
width="80"
height="80"
alt="Loading avatar"
/>
<!-- Hero banner skeleton -->
<img
src="https://fallback.pics/api/v1/animated/skeleton/1200x400"
width="1200"
height="400"
alt="Loading header image"
/> Dark mode
Match skeleton colors to your theme
Pass hex colors to control the skeleton base and shimmer highlight. For dark-mode UIs, a dark base with a slightly lighter shimmer looks correct. For light UIs, the default light gray is usually fine.
You can use CSS custom properties in a data-theme attribute or media query to swap which URL is used. The URL is just a string — swap it programmatically when the theme changes.
<!-- Light mode skeleton -->
https://fallback.pics/api/v1/animated/skeleton/600x400/E4E4E7/F4F4F5
<!-- Dark mode skeleton -->
https://fallback.pics/api/v1/animated/skeleton/600x400/27272A/3F3F46
<!-- React with theme-aware URL -->
const skeletonUrl = isDark
? 'https://fallback.pics/api/v1/animated/skeleton/600x400/27272A/3F3F46'
: 'https://fallback.pics/api/v1/animated/skeleton/600x400/E4E4E7/F4F4F5'; React pattern
Skeleton while image loads, then fade in
The simplest skeleton pattern: set the src to the skeleton URL initially, then update it to the real image once it resolves. Because img src is a string, you can update it without unmounting the element. The browser replaces the SVG with the real image in one repaint.
Wrap this in a custom hook to keep components clean. The hook returns the active src and a handler to set the loaded state.
import { useState } from 'react';
function useImageWithSkeleton(realSrc: string, w: number, h: number) {
const skeleton = `https://fallback.pics/api/v1/animated/skeleton/${w}x${h}`;
const [src, setSrc] = useState(skeleton);
const [loaded, setLoaded] = useState(false);
const preload = () => {
const img = new Image();
img.onload = () => { setSrc(realSrc); setLoaded(true); };
img.onerror = () => setSrc(`https://fallback.pics/api/v1/${w}x${h}`);
img.src = realSrc;
};
return { src, loaded, preload };
}
function LazyCard({ realSrc, alt }: { realSrc: string; alt: string }) {
const { src, preload } = useImageWithSkeleton(realSrc, 600, 400);
return (
<img
src={src}
width={600}
height={400}
alt={alt}
onLoad={preload}
/>
);
} Ecommerce grid
Keep product grids stable while images load
Product grids often render before images are fetched. With no placeholder, grid cells collapse to zero height, then jump open — a CLS event that Google measures in Core Web Vitals. Animated skeletons at the final image dimensions prevent that jump entirely.
For a grid of 24 product cards all using the same 300x300 skeleton URL, the browser caches the first response and serves the rest from memory. The animation is embedded in the SVG so it runs in each img independently.
{products.map((product) => (
<article key={product.id} class="product-card">
<img
src={
product.imageUrl
?? 'https://fallback.pics/api/v1/animated/skeleton/300x300'
}
width={300}
height={300}
alt={product.name}
onError={(e) => {
e.currentTarget.src =
'https://fallback.pics/api/v1/animated/skeleton/300x300';
}}
loading="lazy"
/>
<h3>{product.name}</h3>
</article>
))} Accessibility
Screen readers and animated content
An img element with a loading state skeleton should carry an alt attribute that describes what is loading, not the skeleton itself. Use "Loading product image" or a short contextual label rather than an empty alt, which can confuse assistive technology when the image later resolves to meaningful content.
If your users have the prefers-reduced-motion media query set, consider switching from the animated skeleton to the static blur placeholder for those sessions. The URL swap can happen in a small JavaScript snippet that checks window.matchMedia.
const prefersReduced =
window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const placeholderUrl = prefersReduced
? 'https://fallback.pics/api/v1/blur/600x400'
: 'https://fallback.pics/api/v1/animated/skeleton/600x400'; Further reading
When to use skeletons vs other placeholder types
Animated skeletons work best for temporary loading states where real content is definitely coming. For permanent fallbacks — broken CDN assets, missing catalog images — a static colored placeholder or branded fallback is a better choice because it does not imply loading that will never complete.
The comparison between skeleton loaders and static fallbacks comes down to the same question as loading vs error states. Use the right tool for each failure mode.
# Related reading
# https://fallback.pics/docs/
# https://fallback.pics/placeholder-image-api/
# https://fallback.pics/blog/skeleton-placeholder-images-vs-static-fallbacks/
# https://fallback.pics/blog/blur-placeholder-loading-states/ Key takeaways
What to standardize before shipping
- Animated skeleton URLs are self-contained SVGs — no external CSS or JS needed.
- Pass hex color pairs to match your light or dark UI theme.
- Set explicit width and height on img elements to prevent CLS.
- Switch to static blur placeholders for users who prefer reduced motion.
- Use skeletons for loading states, static fallbacks for permanent error states.
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.