How to Add an Image to a Website
Add images with accessible HTML, responsive sizing, srcset, picture, troubleshooting, and a ScreenshotNeo option for generated captures.
The standard way to add an image to a website is an HTML <img> element with a valid src and useful alt text:
<img src="images/photo.jpg" alt="A red bicycle leaning against a brick wall">
The src value points to the image file. Use a relative path for a file in your site, or an absolute HTTPS URL for an image hosted elsewhere. The alt value is the text alternative used by screen readers and shown when the image cannot load. See the MDN <img> reference.
1. Add a local image with HTML
Put this structure in a simple site:
my-site/
├── index.html
└── images/
└── bicycle.jpg
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Product photos</title>
</head>
<body>
<h1>Bicycle collection</h1>
<img
src="images/bicycle.jpg"
alt="A red bicycle leaning against a brick wall"
width="1200"
height="800"
>
</body>
</html>
Paths are resolved from the URL of the HTML document. If index.html is in the site root, images/bicycle.jpg means a file in the root-level images directory. From products/item.html, the same root-level file would usually be ../images/bicycle.jpg. File names and directory names are often case-sensitive on production servers.
Absolute URLs
<img
src="https://cdn.example.com/catalog/bicycle.jpg"
alt="A red bicycle leaning against a brick wall"
>
The remote server must allow the browser to fetch the image over HTTPS. An image URL that requires a private login, blocks hotlinking, or returns HTML instead of image bytes will not render.
2. Write useful alternative text
W3C WAI states: “Images must have text alternatives that describe the information or function represented by them.” Choose the text from the image’s purpose and its surrounding content. The WAI Images Tutorial and H37 technique provide the accessibility guidance.
| Image role | What to write | Example |
|---|---|---|
| Informative | State the important information conveyed by the image. | alt="Bar chart showing sales rising from January to June" |
| Decorative | Use an empty value so assistive technology can skip it. | alt="" |
| Functional | Describe the action or destination, not the pixels. | alt="Open the account settings" |
| Complex chart or diagram | Use a short identifying alt and provide the detailed explanation in nearby text. | alt="System architecture diagram" plus a textual description |
Do not repeat information already stated immediately beside the image, add phrases such as “image of” unnecessarily, or leave meaningful images with an empty alt value. A linked image needs alt text describing what activating the link does:
<a href="/account">
<img src="icons/account.svg" alt="Open your account">
</a>
3. Reserve space with width and height
Use the image’s actual intrinsic dimensions. The browser can reserve the aspect ratio before the file arrives, reducing layout movement:
<img
src="images/photo.jpg"
alt="A red bicycle leaning against a brick wall"
width="1200"
height="800"
>
These attributes describe the source dimensions; they do not force the image to display at 1200 pixels on every screen. CSS controls the rendered size.
4. Make images responsive
img {
max-width: 100%;
height: auto;
display: block;
}
max-width: 100% lets an image shrink to its container while height: auto preserves its proportions. Keep the intrinsic width and height attributes as well. This is the pattern described by W3C’s advisory C37 technique.
For a contained image with a maximum reading width:
.article-image {
width: 100%;
max-width: 800px;
height: auto;
margin-inline: auto;
}
WCAG 2.2’s Reflow criterion uses a 320 CSS pixel viewport width as a test threshold (and 256 CSS pixels for height). Check that the image and its container do not create unwanted horizontal scrolling at narrow widths.
5. Serve different image sizes with srcset and sizes
If you have several resolutions of the same image, provide width candidates:
<img
src="images/landscape-800.jpg"
srcset="images/landscape-400.jpg 400w,
images/landscape-800.jpg 800w,
images/landscape-1600.jpg 1600w"
sizes="(max-width: 600px) 100vw, 800px"
width="1600"
height="1000"
alt="Snow-covered mountain reflected in a lake"
>
srcset lists available files and their intrinsic widths. sizes tells the browser how wide the image is expected to appear. The browser chooses a suitable candidate based on those hints and current conditions. Do not mix width descriptors (w) and pixel-density descriptors (x) in one srcset.
For a fixed display size with density variants, use pixel-density descriptors instead:
<img
src="images/avatar.png"
srcset="images/avatar.png 1x, images/avatar@2x.png 2x"
width="96"
height="96"
alt="Jordan Lee"
>
6. Use <picture> for formats or art direction
Use <picture> when the source itself should change, such as an alternate crop or format:
<picture>
<source
type="image/avif"
srcset="images/hero.avif 800w, images/hero-large.avif 1600w"
sizes="100vw"
>
<source
type="image/webp"
srcset="images/hero.webp 800w, images/hero-large.webp 1600w"
sizes="100vw"
>
<img
src="images/hero.jpg"
alt="A mountain lake at sunrise"
width="1600"
height="900"
>
</picture>
The nested <img> is the fallback and carries the alt text. Use media conditions for art-directed crops:
<picture>
<source media="(max-width: 600px)" srcset="images/portrait-crop.jpg">
<img src="images/wide-crop.jpg" alt="A runner crossing the finish line" width="1600" height="900">
</picture>
7. Images in common HTML contexts
Figure and caption
<figure>
<img src="images/diagram.png" alt="Request flow from browser to image server" width="1200" height="700">
<figcaption>The request passes through the image service before the browser displays the result.</figcaption>
</figure>
Background images
.hero {
background: url("/images/hero.jpg") center / cover no-repeat;
}
CSS backgrounds are appropriate for decoration. If the background conveys information, use an HTML image or provide an equivalent text alternative; CSS alone does not give screen readers a useful alt value.
Lazy loading
<img
src="images/gallery-12.jpg"
alt="A ceramic bowl on a wooden table"
width="1200"
height="800"
loading="lazy"
decoding="async"
>
Lazy-load images below the initial viewport when it helps page weight. Keep the main above-the-fold image eager unless you have a specific reason to defer it.
8. Check image files and delivery
- Use a format supported by your target browsers and pipeline, such as JPEG for photographs, PNG for lossless transparency, SVG for scalable vector artwork, and WebP or AVIF when your delivery setup supports them.
- Export dimensions close to the largest size actually displayed. Sending a 6000-pixel original into a 400-pixel card wastes bandwidth.
- Serve images over HTTPS and configure long-lived caching for versioned files.
- Confirm the response has an image content type such as
image/jpeg,image/png,image/webporimage/svg+xml. - Keep filenames URL-safe. Encode spaces or rename files to avoid path and deployment differences.
9. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Broken-image icon | Wrong relative path, filename case, or missing deployed file | Open the exact image URL in a browser, inspect the Network panel, and compare the path character by character. |
| 404 response | The server cannot find the requested resource | Correct the directory level (./, ../, or root-relative path) and deploy the file. |
| 403 response | Private object, hotlink protection, or authorization requirement | Make the asset readable to site visitors or serve it through an authorized image endpoint. |
| Image downloads instead of displaying | Incorrect Content-Type or download disposition |
Configure the server to return the correct image media type and inline content. |
| Image is stretched | Both dimensions are forced independently | Set one dimension to auto; include accurate intrinsic width and height. |
| Horizontal scrolling on phones | Fixed width exceeds the viewport | Apply max-width: 100%; height: auto and test at 320 CSS pixels. |
| Screen reader says “image” with no meaning | Missing or empty alt on an informative image | Write concise purpose-based alt text. |
| Decorative image is announced | Decorative image has descriptive alt text | Use alt="" when it adds no information. |
| Wrong responsive file | Incorrect sizes, mixed descriptor types, or inaccurate file widths |
Check the rendered CSS width, use only one descriptor type, and verify each candidate’s real dimensions. |
| Slow page | Oversized files, too many images, or no caching | Resize and compress assets, use responsive candidates, lazy-load below-fold images, and cache versioned files. |
10. Capture a website image with ScreenshotNeo
If your goal is to place a current webpage snapshot into another site, you can capture the source page instead of maintaining a browser automation setup. ScreenshotNeo is a website screenshot API and MCP server. It accepts one GET request and returns PNG, JPEG, WebP, or PDF. Cookie and consent banners, newsletter popups, and chat widgets are removed before capture; bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Responses identify the page verdict and billing status with X-Page-Verdict and X-Billed headers.
See the ScreenshotNeo API documentation for the full parameter list. The same service supports full-page captures with lazy images loaded, CSS-selector element captures, dark mode, device presets and custom viewports, retina scale, PDF paper settings, custom CSS and JavaScript, clicks, waits, request blocking, headers, cookies, user agents, authorization, timezone, geolocation, transparent backgrounds, resizing, TTL caching, signed image links, asynchronous jobs with signed webhooks, bulk capture of up to 100 URLs per call, usage reporting, and an OpenAPI specification.
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 buffer = Buffer.from(await res.arrayBuffer());
require('node:fs').writeFileSync('shot.webp', buffer);
Insert the returned file
<img
src="/captures/stripe.webp"
alt="Screenshot of the Stripe homepage"
width="1600"
height="1000"
>
For a public <img> tag, use a signed link. For repeated captures, choose a cache TTL; for many URLs, use the bulk endpoint. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools to Claude, Cursor, and other MCP clients.
Or skip the browser setup
Use the one-call API example above when you need a maintained capture service. Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. The MCP server lets AI agents take screenshots. The Free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
11. Performance, reliability, and cost checklist
- Performance: provide actual dimensions, select an appropriate source with
srcset, avoid oversized originals, and lazy-load images below the fold. - Reliability: test production URLs, case-sensitive paths, fallback formats, and slow or blocked networks. Keep meaningful alt text so the page still communicates when an image fails.
- Accessibility: decide whether the image is informative, decorative, functional, or complex before writing alt text. Test keyboard and screen-reader flows for linked images.
- Capture cost: ScreenshotNeo bills only clean shots; bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing. Use caching and bulk capture where they fit your workload.
12. Final checklist
- The
srcURL returns the intended image. - The alt value matches the image’s purpose; decorative images use
alt="". - Intrinsic width and height are accurate.
- CSS prevents overflow and preserves the aspect ratio.
srcsetandsizesuse matching, verified candidates when needed.- The image remains understandable if it cannot load.
- The page works at narrow widths and with assistive technology.
FAQ
Can I add an image without CSS?
Yes. A valid <img> element works without CSS, though responsive CSS is recommended for images that must fit different screens.
Should every image have alt text?
Every <img> should have an alt attribute. Use meaningful text for informative or functional images and an empty value for purely decorative images.
Is a URL in src enough?
It is enough for a basic image if the URL is publicly fetchable and returns a supported image file. Add dimensions, responsive candidates, and appropriate alt text as the page requires.
When should I use <picture> instead of srcset?
Use srcset for different resolutions of the same image. Use <picture> when the format, crop, or source should change.
Can ScreenshotNeo return an image I can embed?
Yes. The API returns PNG, JPEG, or WebP bytes, and signed links are available for public image tags.


