Placeholder Image Generator
SVG placeholders with no request to anybody: a few hundred bytes in your own page instead of a round trip to a service that may not be there next year.
Size 800 × 450
Aspect ratio 1.7778 (16:9)
Pattern diagonal
SVG size 642 bytes
SVG
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 450" width="800" height="450" role="img" aria-label="800 × 450">
<defs>
<pattern id="ph-diagonal-800x450" width="16" height="16" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<line x1="0" y1="0" x2="0" y2="16" stroke="#8b93a7" stroke-width="6" />
</pattern>
</defs>
<rect width="800" height="450" fill="#e6e9ef" />
<rect width="800" height="450" fill="url(#ph-diagonal-800x450)" opacity="0.35" />
<text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" font-family="system-ui, sans-serif" font-size="56" fill="#8b93a7">800 × 450</text>
</svg>
As an img, with the dimensions that stop the layout jumping
<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 800 450' width='800' height='450' role='img' aria-label='800 × 450'%3E %3Cdefs%3E %3Cpattern id='ph-diagonal-800x450' width='16' height='16' patternUnits='userSpaceOnUse' patternTransform='rotate(45)'%3E %3Cline x1='0' y1='0' x2='0' y2='16' stroke='%238b93a7' stroke-width='6' /%3E %3C/pattern%3E %3C/defs%3E %3Crect width='800' height='450' fill='%23e6e9ef' /%3E %3Crect width='800' height='450' fill='url(%23ph-diagonal-800x450)' opacity='0.35' /%3E %3Ctext x='50%25' y='50%25' dominant-baseline='middle' text-anchor='middle' font-family='system-ui, sans-serif' font-size='56' fill='%238b93a7'%3E800 × 450%3C/text%3E %3C/svg%3E" width="800" height="450" alt="" />
As a CSS background
.placeholder {
aspect-ratio: 800 / 450;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 800 450' width='800' height='450' role='img' aria-label='800 × 450'%3E %3Cdefs%3E %3Cpattern id='ph-diagonal-800x450' width='16' height='16' patternUnits='userSpaceOnUse' patternTransform='rotate(45)'%3E %3Cline x1='0' y1='0' x2='0' y2='16' stroke='%238b93a7' stroke-width='6' /%3E %3C/pattern%3E %3C/defs%3E %3Crect width='800' height='450' fill='%23e6e9ef' /%3E %3Crect width='800' height='450' fill='url(%23ph-diagonal-800x450)' opacity='0.35' /%3E %3Ctext x='50%25' y='50%25' dominant-baseline='middle' text-anchor='middle' font-family='system-ui, sans-serif' font-size='56' fill='%238b93a7'%3E800 × 450%3C/text%3E %3C/svg%3E");
background-size: cover;
}
642 bytes of markup, and no request to anybody. A placeholder from a
third-party service costs a DNS lookup, a connection and a round trip
per image, and hands the visitor's IP address and referrer to a company
they have never heard of. Several of the popular ones have also simply
stopped working, which takes every mock-up with them.
Use the final dimensions. 800 × 450 here means the box reserves the
right space, so nothing moves when the real image arrives. A placeholder
at a convenient size hides the layout shift until production, which is
where Cumulative Layout Shift gets measured.
The width and height attributes on the img are what give the browser the
aspect ratio before anything loads. They are not a styling instruction:
CSS can still size the element, and the attributes stop the reflow.
alt="" on a decorative placeholder is correct: it tells a screen reader
to skip an image that carries no information. When the real image goes
in, the alt text goes in with it, and an empty alt on a meaningful image
is a failure rather than a neatness.
The data URI is percent encoded rather than base64: smaller, and a
colour can be edited in place in the stylesheet. The hash in a colour
has to be written as %23 or the URL ends there, which is the usual
reason an inline SVG background silently fails.
Do not ship placeholders. They are for wireframes and for tests. A grey
box with its dimensions in the middle is unmistakable in a design review
and embarrassing in production, which is the argument for using
something this ugly on purpose.
Output is valid and updates as you type.
Fix the highlighted fields to update the output.
The usual placeholder is a URL to somebody else’s server. That means a DNS lookup, a connection and a round trip for every box on the page, the visitor’s IP address and referrer handed to a company they have never heard of, and a mock-up that breaks the day the service changes its URL scheme. Several of the popular ones have simply stopped working, taking every wireframe with them.
An SVG placeholder is a few hundred bytes of markup in your own page. It scales, it costs nothing, it works offline, and it can be a data URI in a stylesheet.
The one thing worth doing carefully is the size: a placeholder at the final dimensions reserves the right space, so nothing moves when the real image arrives.
How to use
- Put in the width and pick a ratio, or set the height yourself.
- Choose a pattern. They are deliberately ugly.
- Copy the SVG, the
imgtag, or the CSS background.
Example
800 × 450 with a diagonal pattern, 642 bytes:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 450" width="800" height="450" role="img" aria-label="800 × 450">
<defs>
<pattern id="ph-diagonal-800x450" width="16" height="16" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<line x1="0" y1="0" x2="0" y2="16" stroke="#8b93a7" stroke-width="6" />
</pattern>
</defs>
<rect width="800" height="450" fill="#e6e9ef" />
<rect width="800" height="450" fill="url(#ph-diagonal-800x450)" opacity="0.35" />
<text x="50%" y="50%" dominant-baseline="middle" text-anchor="middle" font-family="system-ui, sans-serif" font-size="56" fill="#8b93a7">800 × 450</text>
</svg>
and as an image, with the two attributes that stop the layout jumping:
<img src="data:image/svg+xml,%3Csvg…" width="800" height="450" alt="" />
Pitfalls
Use the final dimensions. A placeholder at a convenient size hides the layout shift until production, which is where Cumulative Layout Shift gets measured. If you do not know the final size, that is the thing to settle before the markup.
width and height on the img are not styling. They give the browser the aspect ratio
before anything loads, and CSS can still size the element. Leaving them off is the single most
common cause of a page that jumps.
alt="" is correct here. An empty alt tells a screen reader to skip an image that carries
no information. When the real image goes in, real alt text goes in with it, and an empty alt on
a meaningful image is a failure rather than a tidiness.
Encode the hash. In a data URI inside url(), a # starts a fragment, so
fill="#e6e9ef" truncates the SVG at the colour and the background silently does not appear.
The generated URI writes %23.
Ids have to be unique on the page. Two placeholders with the same pattern id and the second one wins for both. The generated id includes the pattern and the size, which helps and is not a guarantee.
Do not ship them. A grey box with its dimensions in the middle is unmistakable in a design review and embarrassing in production. That is the argument for using something this ugly on purpose: nobody mistakes it for a design.
A placeholder is not a loading state. For an image that is genuinely still loading, a blurred tiny version or a solid average colour reads better, and both need the real image to generate them.
Compatibility
Arithmetic and string handling in the browser: nothing is uploaded, nothing is stored, and no request is made anywhere.
SVG patterns, dominant-baseline and aspect-ratio are in every current browser. The
patternTransform on the diagonal fill is the standard way to rotate a pattern without
rotating the shape it fills.
The data URI is percent encoded rather than base64: smaller, and the colours stay readable and editable in the stylesheet. Base64 for an SVG is a habit from worse tooling.
Font size is derived from the smaller side, so the label stays proportionate on a banner and on
a thumbnail, with a floor of 12 so it is legible on something tiny. font-family: system-ui
means the label renders in whatever the operating system uses and needs no font to be
available.
Rendering was checked in a browser: all four patterns come out at the requested size with the
label centred, measured with getBBox rather than by eye.
Frequently asked questions
Can I use these in production as a fallback?
alt empty only if the
surrounding text already says what is missing.