How to Create and Display Images on a Website with HTML
Learn how to add, resize, optimize, and troubleshoot HTML images with responsive, accessible patterns you can use in production.

The standard way to display a content image is an escaped HTML <img> element with a usable src and meaningful alt text:
<img src="images/coast.jpg" alt="Rocky coastline at sunset" width="1200" height="800">
The src identifies the image file. The alt value supplies replacement text when the image cannot be seen or loaded. Adding intrinsic width and height lets the browser reserve the correct aspect ratio before downloading the file, which helps prevent layout movement. See the MDN img reference.
1. Add a basic image
Place the image file in your project and point src to its relative or absolute URL.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Image example</title>
</head>
<body>
<main>
<h1>Coastline</h1>
<img
src="images/coast.jpg"
alt="Rocky coastline at sunset"
width="1200"
height="800"
>
</main>
</body>
</html>
Write useful alt text
Describe the image’s relevant meaning in its surrounding context. If the image fails, the text should still communicate what a reader needs to know. Avoid repeating nearby captions or adding file names such as IMG_2048.jpg. For decorative imagery, follow your project’s accessibility guidance for the appropriate treatment.
Use correct paths
images/coast.jpgis relative to the current page URL./images/coast.jpgis relative to the site’s root.https://cdn.example.com/coast.jpgis an absolute URL.
Check capitalization: many production servers treat Coast.jpg and coast.jpg as different files.
2. Reserve space with width and height
Set the image’s intrinsic dimensions even when CSS later scales it. The browser calculates the aspect ratio and reserves space while the resource is downloading.
<img src="images/product.jpg" alt="Blue backpack on a wooden bench" width="1600" height="1200">
Do not change the meaning of these attributes to force a crop. If the source is 1600 by 1200, use those intrinsic dimensions. Control the rendered size with CSS:
img {
max-width: 100%;
height: auto;
display: block;
}
3. Make images responsive with srcset and sizes
When you have multiple files, srcset gives the browser candidates and sizes describes the image’s expected rendered width. The browser can then choose a suitable resource for the viewport and display density. The width descriptors must match the actual pixel widths of the files. See MDN responsive images.

<img
src="images/coast-800.jpg"
srcset="images/coast-480.jpg 480w,
images/coast-800.jpg 800w,
images/coast-1200.jpg 1200w"
sizes="(max-width: 600px) 100vw, 800px"
alt="Rocky coastline at sunset"
width="1200"
height="800"
>
In this example, a viewport up to 600 pixels uses the full viewport width; larger viewports reserve an 800-pixel slot. Adjust sizes to your actual layout. A wrong value can cause an unnecessarily large or small file to be selected.
Fixed-size images with density descriptors
For an image rendered at a fixed CSS size, density descriptors such as 1x and 2x provide standard and high-resolution alternatives:
<img
src="images/avatar-1x.jpg"
srcset="images/avatar-1x.jpg 1x, images/avatar-2x.jpg 2x"
alt="Sam Lee"
width="96"
height="96"
>
4. Use picture for alternate formats or crops
Use <picture> when the source should change by format or art direction. Keep an escaped <img> fallback inside it; that element supplies the displayed image and alternative text. See the MDN picture reference.
<picture>
<source srcset="images/coast.avif" type="image/avif">
<source srcset="images/coast.webp" type="image/webp">
<img src="images/coast.jpg"
alt="Rocky coastline at sunset"
width="1200"
height="800">
</picture>
For art direction, provide different crops with media conditions:
<picture>
<source media="(max-width: 600px)" srcset="images/coast-portrait.jpg">
<img src="images/coast-wide.jpg" alt="Rocky coastline at sunset" width="1600" height="900">
</picture>
5. Lazy-load images below the first viewport
loading="lazy" asks the browser to defer fetching an image until it is near the viewport. Keep dimensions on lazy images so their space is known before loading. Use lazy loading for content that can wait, such as a gallery farther down the page; the main above-the-fold image usually should load immediately. See MDN lazy loading.
<img
src="images/gallery-1.jpg"
alt="A red canoe beside the shore"
width="1200"
height="800"
loading="lazy"
>
6. Choose HTML images or CSS backgrounds
Use an HTML image when the picture is content that belongs in the document. Use CSS background-image for presentation or layout decoration, such as a hero backdrop behind text. CSS backgrounds do not provide the same replacement-text mechanism as an HTML content image.
.hero {
min-height: 20rem;
background: url("images/hero.jpg") center / cover no-repeat;
}
7. Complete gallery example
<section aria-labelledby="gallery-title">
<h2 id="gallery-title">Shoreline gallery</h2>
<figure>
<img
src="images/shore-800.jpg"
srcset="images/shore-480.jpg 480w, images/shore-800.jpg 800w, images/shore-1200.jpg 1200w"
sizes="(max-width: 700px) 100vw, 700px"
alt="Waves breaking against dark rocks"
width="1200"
height="800"
>
<figcaption>Waves breaking against dark rocks.</figcaption>
</figure>
<img src="images/canoe.jpg" alt="A red canoe beside the shore" width="1200" height="800" loading="lazy">
</section>
8. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Broken-image icon | Wrong path, filename, case, or server response | Open the exact URL directly, check spelling and capitalization, and confirm the file is deployed. |
| Image stretches or looks squashed | Width and height ratio does not match the source | Use the source’s intrinsic ratio and set height: auto in CSS. |
| Layout jumps while loading | No intrinsic dimensions | Add accurate width and height attributes. |
| Mobile downloads an unnecessarily large file | Only one large src is supplied or sizes is inaccurate |
Generate width variants, add srcset, and describe the real slot with sizes. |
| Modern format fails in some browsers | No fallback source | Put AVIF/WebP sources before a broadly supported fallback inside <picture>. |
| Lazy image never appears | Invalid URL, blocked request, or script/CSS hiding it | Inspect the network panel and computed styles; remove unnecessary scripts and verify the URL. |
| Image is missing from search or accessibility output | Meaningful alternative text is absent | Add concise context-specific alt text. |
9. Performance, reliability, and cost checklist
- Serve an image close to the rendered slot size; use
srcsetfor several widths. - Set
sizesto match the layout at each breakpoint. - Include intrinsic dimensions on every content image, including lazy images.
- Use
picturefor format negotiation or genuinely different crops. - Lazy-load images that start below the viewport, while allowing critical images to load normally.
- Keep stable, cacheable URLs and verify that deployed paths work over HTTPS.
- Do not claim a performance gain from a pattern without measuring your page and its actual assets.
The HTML markup itself has no product cost; your hosting, storage, bandwidth, image processing, and any CDN determine operational cost. Smaller correctly selected files reduce transfer work, but keep enough resolution for the displayed slot and the reader’s device.
10. Or skip the browser setup
If you need a screenshot of a finished page rather than hand-authored image markup, ScreenshotNeo provides a website screenshot API. One GET request returns PNG, JPEG, WebP, or PDF. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and responses identify the result with X-Page-Verdict and X-Billed headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

See the ScreenshotNeo API documentation for all options.
cURL
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
Node.js
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const data = Buffer.from(await res.arrayBuffer());
require('fs').writeFileSync('shot.webp', data);
ScreenshotNeo also supports full-page capture with lazy images loaded, CSS-selector element capture, dark mode, device presets and custom viewports, retina scale, custom CSS and JavaScript, clicks, waits, blocked requests, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, configurable caching, signed links, asynchronous jobs with signed webhooks, bulk capture for up to 100 URLs, a usage API, and an OpenAPI specification. Clean shots are the only billed shots. One thousand screenshots per month are free with no card; paid plans start at $5 for 3,000. Create your free ScreenshotNeo account.
11. FAQ
Should every image have alt text?
Every content image needs replacement text that conveys its relevant meaning. For decorative images, follow your project’s accessibility guidance.
Is width and height enough to make an image responsive?
No. They reserve the aspect ratio. Use CSS sizing and, when several files exist, srcset and sizes to select an appropriate resolution.
When should I use picture instead of srcset?
Use picture when you need alternate formats or different crops. Use srcset and sizes when the same composition is available at multiple resolutions.
Can CSS backgrounds replace img?
Use backgrounds for decorative or layout imagery. Use an HTML image when the image is content that readers need to understand.
Where should lazy loading be used?
Use it for images that begin below the first viewport and can wait. Keep dimensions on those images, and avoid delaying critical content.


