HTML Embedding Graphics

Not every image on a web page comes from a single <img> tag — HTML actually gives you several different ways to bring graphics onto a page, and each one works differently under the hood. Some load an external file and drop it in as a self-contained unit; others let you write the actual shapes as markup that becomes part of the page’s own structure. Knowing which tool fits which job — a photograph, an icon that needs to change color on hover, a chart, or a blank canvas for JavaScript drawing — is essential for building fast, accessible, and maintainable pages.

Overview: How Graphics Get Onto a Page

HTML has no single “picture” element that does everything. Instead it gives you a family of elements, and the right choice depends on where the graphic data lives and what you need to do with it afterward:

  • <img> — embeds a raster or vector image file (JPEG, PNG, GIF, WebP, SVG) referenced by URL. The browser fetches the file and paints it as a single opaque box.
  • <svg> — writes vector shapes directly as markup, inline in the document. Unlike an image file, this is not a black box: every <circle>, <rect>, or <path> becomes a real node in the DOM.
  • <canvas> — reserves a blank rectangular drawing surface with no visible content of its own; JavaScript draws pixels onto it using the Canvas API.
  • <object> — a general-purpose embedding element that can display images, SVG files, or other resources, and supports genuine fallback content for browsers that can’t render the resource.
  • <embed> — a simpler, older embedding element for external resources (often plugins historically); it has no fallback-content mechanism.

The distinction between <img>/<canvas>/<object> on one side and <svg> on the other matters a lot for rendering. The first group are what the rendering engine calls replaced elements: the browser’s layout box exists, but its visual content comes from somewhere else (a fetched file, or pixels painted by script) rather than from child nodes the HTML parser built. You cannot select the individual pixels of a JPEG with CSS, and you cannot inspect “the second circle” inside an <img src="chart.svg"> in DevTools — as far as the DOM is concerned, it’s one leaf node.

Inline <svg> is different. When the HTML parser reaches an <svg> tag, it switches into a special “foreign content” parsing mode and builds a real subtree of SVG DOM nodes, each with its own attributes, computed styles, and event listeners. That’s why a CSS rule targeting svg circle:hover actually works, or why document.querySelector can reach a shape inside inline SVG — something that’s impossible with an <img> pointing at an external SVG file.

Syntax

Each embedding element has its own attribute set. Here is the core syntax for each:

<img src="path/to/file.jpg" alt="description" width="w" height="h">

<svg viewBox="minX minY width height" width="w" height="h">
  <!-- shape elements go here -->
</svg>

<canvas width="w" height="h">
  Fallback content for non-supporting browsers
</canvas>

<object data="path/to/file" type="mime/type" width="w" height="h">
  Fallback content
</object>
Attribute Used on Purpose
src img URL of the image file to fetch and display.
alt img Text alternative read by screen readers and shown if the image fails to load; required for accessibility.
width / height img, svg, canvas, object Intrinsic box size in pixels; on canvas these set the actual coordinate-space resolution, not just the display size.
viewBox svg Defines the internal coordinate system shapes are drawn in, independent of the displayed width/height.
data object URL of the resource to embed.
type object, embed MIME type of the resource (e.g. image/svg+xml), helping the browser choose how to render it.
loading img lazy defers off-screen image loading until the user scrolls near it.

Examples

Example 1: A photograph with a caption

<figure>
  <img src="mountain-sunrise.jpg" alt="Sunrise over a mountain range with an orange sky" width="800" height="450" loading="lazy">
  <figcaption>Sunrise over the Rockies, photographed at 6:12 AM.</figcaption>
</figure>

Result: The browser fetches mountain-sunrise.jpg, reserves an 800×450 box for it before the file arrives (preventing layout shift), and displays the photo with a caption line directly underneath it, visually grouped as a single figure unit.

This is the most common way to embed a graphic: reference an external file by URL. The alt text is not decorative — if the image fails to load, or is read by a screen reader, that sentence stands in for the picture. The explicit width and height let the browser calculate the image’s aspect ratio and allocate space in the layout immediately, before the actual bytes have downloaded.

Example 2: Inline vector graphics

<svg viewBox="0 0 100 100" width="120" height="120" role="img" aria-label="A blue circle labeled SVG">
  <circle cx="50" cy="50" r="40" fill="#3b82f6" stroke="#dc2626" stroke-width="4"></circle>
  <text x="50" y="55" text-anchor="middle" fill="#ffffff" font-size="14">SVG</text>
</svg>

Result: A 120×120 pixel graphic appears showing a solid blue circle with a 4-pixel-wide red outline, with the white text “SVG” centered inside it.

Because this markup is written inline, the browser parses <circle> and <text> as genuine DOM elements in the SVG namespace, siblings of the rest of the page’s DOM tree. The viewBox="0 0 100 100" defines an internal 100-by-100 coordinate grid that the shapes are positioned on, which is then scaled to fit the displayed 120×120 pixel box — this is why SVG stays crisp at any zoom level or screen density, unlike a raster <img>.

Example 3: Embedding an external resource with fallback content

<object data="quarterly-chart.svg" type="image/svg+xml" width="400" height="300">
  <p>Your browser can't display this chart. <a href="quarterly-chart.svg">Download the SVG file</a> instead.</p>
</object>

Result: Normally, a 400×300 box appears showing the rendered chart from quarterly-chart.svg. If the browser cannot load or render that resource, it instead displays the paragraph with the download link — real fallback content, not just alternative text.

<object> is the only one of these embedding elements whose children are true fallback content: they only render if the primary resource fails. This makes it useful for embedding SVG files, PDFs, or other documents where you want a graceful, informative failure state rather than a broken-image icon.

Example 4: A canvas drawing surface

<canvas width="400" height="200">
  A bar chart comparing quarterly sales figures for 2025.
</canvas>

Result: On its own, this markup renders as a blank 400×200 pixel rectangle — <canvas> has no visual content until JavaScript draws onto it. If a browser doesn’t support canvas at all, the descriptive sentence inside it is shown instead.

The width and height attributes on <canvas> are special: they define the actual pixel grid that drawing commands operate on, not just a CSS display size. Setting the size later with CSS instead of these attributes stretches the existing pixel grid and makes drawings look blurry. This course covers markup only, but it’s worth knowing the drawing itself is done with JavaScript’s Canvas API, covered in the JavaScript course.

How It Works Step by Step

When the HTML parser encounters each of these elements, it behaves differently:

  • img: the parser creates a single element node, then the browser’s resource loader fetches src in parallel with the rest of the page parsing. Once bytes arrive, the image is decoded and painted into the reserved layout box. Nothing inside the file becomes part of the DOM.
  • svg: the HTML parser switches to the SVG “foreign content” insertion mode for everything between <svg> and </svg>, building a full subtree of real elements (in the SVG namespace) that participate in normal DOM traversal, CSS cascading, and event dispatch.
  • canvas: the parser creates one element node with an empty bitmap. The bitmap is only ever touched by script calling methods like getContext("2d") — the markup by itself is inert.
  • object / embed: the browser requests the resource named in data/src and, based on the type (or a sniffed MIME type from the response), hands it to the appropriate renderer — the same engine that renders images, or a nested document.

In every replaced-element case (img, canvas, object, embed), the box the browser lays out is reserved as soon as the element is parsed — using the width/height attributes if present — even though the actual visual content may arrive, or get drawn, much later.

Common Mistakes

Mistake 1: Treating a void element as if it had content

<img> and <embed> are void elements — they never have a closing tag and cannot contain children. Writing:

<img src="logo.png" alt="Company logo">
  <span>Our logo</span>
</img>

is invalid: there is no such thing as content “inside” an <img>, so the parser does not treat the <span> and stray </img> as related to the image at all — they just become loose markup sitting after it. The fix is to group the image with its caption using an actual container element:

<figure>
  <img src="logo.png" alt="Company logo">
  <figcaption>Our logo</figcaption>
</figure>

Mistake 2: Expecting embed to provide fallback content

Unlike <object>, <embed> is also a void element with no fallback mechanism, so this doesn’t do what it looks like it should:

<embed src="diagram.svg" type="image/svg+xml">
  <p>Fallback text describing the diagram</p>
</embed>

The paragraph is not conditional fallback — <embed> can’t have children at all, so this markup is malformed and the paragraph just ends up as ordinary content next to it, always visible. When you need genuine “show this only if the resource fails” behavior, use <object> instead, which is designed to support real fallback children:

<object data="diagram.svg" type="image/svg+xml" width="300" height="200">
  <p>Fallback text describing the diagram</p>
</object>

Best Practices

  • Always write meaningful alt text for <img>; use alt="" only for purely decorative images so screen readers skip them.
  • Set width and height on <img>, <svg>, and <object> so the browser can reserve layout space before the resource loads, avoiding content jumping around (layout shift).
  • Use inline <svg> when you need to style individual shapes with CSS, animate them, or manipulate them with JavaScript; use <img src="icon.svg"> for simple, static vector graphics you don’t need to touch.
  • Add loading="lazy" to <img> elements that sit far down the page to defer their network cost.
  • Give <object> real, informative fallback content instead of leaving it empty, especially when embedding formats not every browser supports.
  • Use <canvas> only when you actually need to draw dynamically with JavaScript — for static graphics, an <img> or inline <svg> is simpler, more accessible, and indexable by search engines.
  • Keep visual styling (colors, borders, shadows) in CSS rather than attributes where possible — this HTML course covers structure and markup; presentation belongs in the CSS course.

Practice Exercises

  1. Build a <figure> containing an <img> of a network diagram (make up a filename), meaningful alt text, width/height attributes, and a <figcaption>.
  2. Write an inline <svg> with a viewBox of 0 0 200 100 containing a rectangle and a circle side by side. Give the <svg> a role and aria-label describing what it shows.
  3. Embed an external SVG chart using <object> with a data, type, width, and height, and write a fallback paragraph with a download link. Then explain in one sentence why <embed> would not give you the same fallback behavior.

Summary

  • <img> embeds an external raster or vector file as a single, opaque replaced element.
  • Inline <svg> becomes real DOM nodes you can style and script individually, unlike an image file.
  • <canvas> is a blank pixel surface with no content until JavaScript draws on it.
  • <object> embeds external resources and supports genuine fallback content; <embed> does not.
  • <img> and <embed> are void elements — they can never have closing tags or children.
  • Always set width/height and provide alt text or fallback content for accessibility and stable layout.