SVG image element: put a PNG or JPG inside an SVG

The SVG image element places a PNG, JPG or another SVG inside a drawing. Its attributes, fitting and cropping, clipping to a shape, and embedding as base64.

Published

The <image> element puts a picture inside an SVG drawing: a PNG, a JPG or another SVG file. It takes the picture’s address in href and a rectangle to draw it in.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <image href="photo.png" x="10" y="10" width="80" height="40"/>
</svg>

The photo is drawn 80 units wide and 40 high, with its top-left corner at 10,10.

This is not the HTML <img> tag, which works the other way round and shows an SVG file on a web page. For that, see SVG in HTML. The two are easy to confuse, and each fails in the other’s place: src does nothing on an <image>, and an <img> written inside an inline <svg> breaks the drawing. In Chrome the HTML parser moved the <img> out of the SVG and took every shape after it out as well.

The attributes

AttributeMeaningDefault
hrefThe address of the picture: a file, a full URL or a data: URINone
x, yThe top-left corner of the box0
width, heightThe size of the boxThe picture’s own size
preserveAspectRatioHow the picture is fitted when the box is a different shapexMidYMid meet

Older files write xlink:href in place of href. Browsers accept both. The SVG 2 specification requires viewers to support at least PNG, JPEG and SVG pictures here.

Leave out width and height and the picture is drawn at its own size, one unit of the drawing for each pixel. A 40 by 20 pixel PNG took a 40 by 20 box in Chrome. Give only width and the height follows from the picture’s proportions. Software other than browsers may want both, so write both in a file that will travel.

Fitting: the picture never stretches by default

When the box and the picture have different proportions, the picture is scaled to fit inside the box and centred, with empty space left over. It is not distorted. preserveAspectRatio changes that:

ValueResult
xMidYMid meet (default)The whole picture is visible, centred, with space at two sides
xMinYMin meetThe same, pushed to the top-left of the box
xMidYMid sliceThe picture fills the box and the overflow is cropped off
noneThe picture is stretched to the box exactly

Tested with a picture twice as wide as it is high in a square box: the default left a band of background above and below, slice filled the square and lost the left and right edges, and none squashed the picture into the square. slice is the equivalent of object-fit: cover in CSS and meet of object-fit: contain. preserveAspectRatio lists all the values.

A picture as the background of a drawing

Put the <image> first, so that everything else is painted over it, and make it cover the whole viewBox:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <image href="photo.png" width="100%" height="100%" preserveAspectRatio="xMidYMid slice"/>
  <circle cx="50" cy="50" r="10" fill="crimson"/>
</svg>

The photo fills the drawing edge to edge and the red dot sits on top of it.

A picture inside a shape

An image is always a rectangle. To show it in a circle, a star or any path, clip it. A <clipPath> holds the shape, and clip-path on the image applies it:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <clipPath id="round">
    <circle cx="50" cy="50" r="40"/>
  </clipPath>
  <image href="photo.png" x="10" y="10" width="80" height="80"
    preserveAspectRatio="xMidYMid slice" clip-path="url(#round)"/>
</svg>

A round portrait. Swap the circle for a <path> and the photo takes that outline instead: a star-shaped path was tested the same way. For rounded corners, clip with a <rect> that has rx. SVG mask and clip-path covers clipping, and masks for soft edges.

The other way is to use the picture as a fill, through a pattern:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <defs>
    <pattern id="photo" width="1" height="1" patternContentUnits="objectBoundingBox">
      <image href="photo.png" width="1" height="1" preserveAspectRatio="xMidYMid slice"/>
    </pattern>
  </defs>
  <circle cx="50" cy="50" r="40" fill="url(#photo)" stroke="black"/>
</svg>

The circle is filled with the photo and keeps its own outline. This suits a polygon or path that should carry a stroke, or several shapes sharing one picture. SVG pattern explains the units.

Linked or embedded

href="photo.png" is a link: the SVG holds only the address, and the picture is a second file that must travel with it. That link breaks in two common situations.

  • The SVG is moved or sent without the picture. The box is then empty.
  • The SVG is shown as an image. An SVG displayed through an HTML <img> tag or a CSS background is not allowed to load other files. In the test, an SVG with a linked PNG showed the photo when opened directly and in an <object>, and showed nothing in its place inside an <img>.

The cure for both is to embed the picture in the SVG as a data: URI, which is the picture’s bytes written out as text:

<image href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUg..." width="100" height="100"/>

The same test file with its PNG embedded this way showed the photo inside an <img>. The cost is size: base64 writes every 3 bytes as 4 characters, so the picture takes a third more room. The test PNG of 129 bytes became 172 characters. Vector editors do this for you: Inkscape, for one, asks whether to embed or link when a bitmap is imported.

From JavaScript

Create the element in the SVG namespace and set href like any other attribute:

const image = document.createElementNS('http://www.w3.org/2000/svg', 'image');
image.setAttribute('href', '/photos/cat.jpg');
image.setAttribute('x', 10);
image.setAttribute('y', 10);
image.setAttribute('width', 80);
image.setAttribute('height', 40);
document.querySelector('svg').append(image);

To change the picture later, set href again. The element fires load when the picture arrives and error when it cannot be fetched, as an HTML image does.

To embed a picture the visitor chooses, read it as a data URI:

<input type="file" id="pick" accept="image/png,image/jpeg">
<svg id="drawing" viewBox="0 0 100 100" width="300"></svg>
<script>
  document.querySelector('#pick').addEventListener('change', (event) => {
    const reader = new FileReader();
    reader.addEventListener('load', () => {
      const image = document.createElementNS('http://www.w3.org/2000/svg', 'image');
      image.setAttribute('href', reader.result);
      image.setAttribute('width', 100);
      image.setAttribute('height', 100);
      document.querySelector('#drawing').append(image);
    });
    reader.readAsDataURL(event.target.files[0]);
  });
</script>

The chosen picture appears in the drawing, and because its data is in the href, saving the SVG saves the picture with it. SVG and JavaScript shows how to save.

An SVG inside an SVG

href can point at another SVG file. It is drawn as a flat picture, scaled into the box: its shapes cannot be styled or scripted from the outer drawing, and the same no-outside-files rule applies to it. To reuse shapes that should stay live, use <use> or a nested <svg> element instead: see SVG g and use.

A picture in an SVG is still pixels

Wrapping a photo in an SVG does not turn it into vectors. The file has the .svg ending and scales as a box, but the photo inside goes soft when enlarged, exactly as the PNG would. This matters most for cutting machines and plotters, which follow outlines: a file that is only an <image> gives them no outline to follow.

To get real vector shapes from a picture, trace it. Image to SVG does that in the browser, and how to convert a picture to SVG explains the choices. To check what a file contains, open it in the SVG viewer and look for <image in its code. Raster and vector explains the difference.

Common problems

What you seeCauseFix
Nothing is drawnsrc used in place of href, or the address is wrongUse href; check the path relative to the SVG file
The picture shows when the SVG is opened, but not on the web pageThe SVG is in an <img> and the picture is a separate fileEmbed the picture as a data: URI
Empty bands beside the pictureThe box and the picture have different proportionspreserveAspectRatio="xMidYMid slice", or match the box to the picture
The picture is squashedpreserveAspectRatio="none"Remove it
Shapes after an <img> vanish<img> is not an SVG elementUse <image href="...">
The picture covers the drawingIt is written last, so painted lastMove the <image> to the top of the code
The SVG file is hugeA photo is embedded as base64Shrink the photo first, or link to it
A cutting program finds nothing to cutThe file holds only a raster pictureTrace it to vectors
A small picture looks blurred when enlargedSmoothingimage-rendering: pixelated in a style keeps hard pixel edges in browsers that support it

Questions

Does <image> need a closing tag? In a standalone file it must be closed, as <image ... /> or <image ...></image>, like every XML element.

Can the picture be transparent? Yes. A PNG’s transparency is kept, and opacity on the element fades the whole picture.

Can I rotate or flip it? Yes, with transform, as for any shape: see SVG transform.

Why is my SVG so large? An embedded photo is the usual reason. Reduce SVG file size shows how to find it.