SVG Bounding Boxes: Measure the Right Coordinates
Compare getBBox and getBoundingClientRect on transformed SVG artwork. Place overlays correctly, test stroke bounds, and fit a viewBox with an explicit margin.
On this page
Use getBBox() when you need an SVG element's bounds in its own user coordinates. Use getBoundingClientRect() when you need a rectangle in CSS pixels relative to the browser viewport. Before copying either result into an overlay or a viewBox, check which coordinate system the destination uses.
A rotated emblem can have a small local box and a much larger screen-aligned box. Both measurements can be valid. Neither should be treated as a universal rectangle around every painted pixel, especially when strokes, markers, clipping, or filters are involved.
Choose the measurement for the destination
| Destination | Starting measurement | Required care |
|---|---|---|
| Rectangle inside the same SVG coordinate system | getBBox() | Give the rectangle the matching transformation context |
| Fixed HTML selection outline | getBoundingClientRect() | Keep it in viewport coordinates and update when layout changes |
| Root SVG viewBox around a transformed group | Local box plus a coordinate transform | Convert into root user units before fitting |
| Crop including strokes and effects | A separately verified bounds workflow | Do not assume a basic geometry box includes all paint |
MDN's getBBox() documentation describes local geometry bounds and the optional stroke, marker, and clipping controls. The measured element's own transform and ancestor transforms are not included in that local result. A group's descendants still contribute to the group's geometry, including their placement within it.
MDN's getBoundingClientRect() reference describes viewport-relative coordinates. Scrolling changes that coordinate relationship. These CSS-pixel values are not automatically SVG user units or physical print dimensions.

Measure an original transformed emblem
This fixture contains a teal ring with a rectangular opening and a separate gold triangle. Both have a thick round-joined stroke. The group is translated, rotated, and scaled.
<svg id="scene" width="600" height="300" viewBox="0 0 300 150"
xmlns="http://www.w3.org/2000/svg" style="overflow:visible">
<g id="art" transform="translate(100 35) rotate(25) scale(1.2)"
stroke="#17364c" stroke-width="10" stroke-linejoin="round">
<path fill="#147d78" fill-rule="evenodd"
d="M0 0H80V60H0Z M20 20H60V40H20Z" />
<path fill="#e5a340" d="M100 50L120 10L140 50Z" />
</g>
</svg>
<script>
const art = document.querySelector("#art");
const local = art.getBBox();
const screen = art.getBoundingClientRect();
</script>The local coordinates run from 0 to 140 horizontally and 0 to 60 vertically. The hole affects the shape's interior, but it does not shrink those outer extents.
We opened this geometry in Chrome and measured it with a small harness. At a 600 × 300 CSS-pixel display size, getBBox() returned x=0, y=0, width=140, height=60. The client rectangle was approximately 365.376 × 272.508 CSS pixels. Its screen position depended on where the SVG sat on the page.
After changing the display size to 300 × 150, the local box stayed 140 × 60. The client rectangle became approximately 182.688 × 136.254, half the previous dimensions. The source geometry had not changed; its mapping to the page had.
These numbers describe this fixture in the tested browser. They are not a comparison of browser engines or a specification for every editor's selection box.
Draw each outline in its own coordinate system
For a local outline, use the returned coordinates and the same transform as the measured group. Place it as a sibling so it does not become part of the next measurement:
const scene = document.querySelector("#scene");
const box = art.getBBox();
const outline = document.createElementNS("http://www.w3.org/2000/svg", "rect");
for (const key of ["x", "y", "width", "height"]) {
outline.setAttribute(key, box[key]);
}
outline.setAttribute("transform", art.getAttribute("transform"));
outline.setAttribute("fill", "none");
outline.setAttribute("stroke", "olive");
scene.append(outline);That direct transform copy is appropriate for this fixture because the artwork and outline share the same parent. A rectangle inserted elsewhere needs a matrix mapping between the two coordinate systems.
For an HTML outline, place a div directly under body with fixed positioning and no transformed containing ancestor:
const box = art.getBoundingClientRect();
Object.assign(overlay.style, {
position: "fixed",
boxSizing: "border-box",
pointerEvents: "none",
border: "2px dashed coral",
left: `${box.x}px`,
top: `${box.y}px`,
width: `${box.width}px`,
height: `${box.height}px`,
});Here overlay is your existing outline element. Re-measure when scrolling, resizing, or changing the artwork's layout or transform. If you use document positioning instead, account for scroll offsets; if you use a positioned container, account for that container's coordinate system. Mixing these placement rules is a common reason an otherwise correct rectangle appears displaced.
Our harness displayed both outlines. The local rectangle tilted with the ring and triangle, while the dashed HTML rectangle stayed horizontal and vertical. The thick stroke extended beyond parts of the geometry outlines.
Test stroke options instead of assuming support
The default local box excludes stroke width. MDN documents getBBox({ stroke: true }), but the options parameter has separate compatibility considerations from the long-established no-argument method.
In our Chrome fixture, passing { stroke: true } returned the same 140 × 60 box as the no-argument call. Removing the 10-unit stroke also left the client rectangle unchanged. The visible dark border clearly changed, so neither result in that test represented the complete painted footprint.
A call that accepts an options object without throwing is not enough to prove that it honored the option. Test a known stroked shape and check the returned dimensions in your target browser. Use the SVG bounding-box definitions to distinguish object, stroke, and decorated bounds.
Do not generalize a half-stroke padding rule to every shape. Miter joins, markers, non-scaling strokes, shadows, and filters can extend differently. For a simple round-joined fixture you control, an explicit margin plus visual inspection may be enough. For an automatic export cropper, those cases need deliberate handling.
Convert a local box before fitting the root viewBox
Never paste the client rectangle's x, y, width, and height directly into the SVG viewBox. Those values include page positioning and use a different unit system.
For the fixture, map the four corners of its local box into the root SVG's user coordinates. MDN's getScreenCTM() reference describes the matrix used to map SVG coordinates toward the document viewport. Combining the artwork matrix with the inverse root matrix removes that shared screen mapping:
const box = art.getBBox();
const rootMatrix = scene.getScreenCTM();
const artMatrix = art.getScreenCTM();
if (!rootMatrix || !artMatrix) throw new Error("SVG is not measurable");
const toRoot = rootMatrix.inverse().multiply(artMatrix);
const corners = [
[box.x, box.y],
[box.x + box.width, box.y],
[box.x + box.width, box.y + box.height],
[box.x, box.y + box.height],
].map(([x, y]) => new DOMPoint(x, y).matrixTransform(toRoot));
const xs = corners.map(p => p.x);
const ys = corners.map(p => p.y);
const x = Math.min(...xs);
const y = Math.min(...ys);
const width = Math.max(...xs) - x;
const height = Math.max(...ys) - y;
const padding = 12; // Chosen for this simple fixture, in root user units.
scene.setAttribute("viewBox", [
x - padding, y - padding,
width + 2 * padding, height + 2 * padding,
].join(" "));This assumes a rendered SVG with invertible two-dimensional transforms. Mapping a local rectangle's corners gives a conservative axis-aligned envelope, not necessarily the tightest possible box around every transformed curve.
The harness produced a viewBox of approximately 57.571 23 206.688 160.254 with that margin. We inspected the result and the fixture's round stroke remained inside the display area. The wide display still had side space because the fitted viewBox had a different aspect ratio; fitting does not imply stretching.
If you need a pivot instead of a crop, the transform-origin guide explains how reference boxes affect rotation. For physical import dimensions, use the SVG size troubleshooting workflow.
Measure the geometry you intend to edit
If your source exists only as a PNG or JPG and you need editable shapes, PerfectVector can recover an SVG before measurement. Inspect its paths, openings, strokes, and any retained raster content. A bounding box tells you where geometry extends; it does not prove that the tracing is accurate or that every visible detail is editable.
An existing SVG can be measured directly. The SVG editing guide helps identify what you actually have, while the image-vectorization overview explains the upstream conversion step.
For raster-only artwork, try your source in PerfectVector, inspect the recovered contours, then measure the group you will place or export. Keep a margin and check the final visible edges before treating the result as a delivery crop.
FAQ
Why do the two methods return different widths? They use different coordinate systems. The local SVG box describes geometry in user units, while the client rectangle describes its viewport-relative bounds in CSS pixels after the page mapping. Scaling and rotation can change those dimensions.
Does a bounding box tell me whether the artwork has a hole? No. A rectangle records outer extents, not interior topology. A solid rectangle and a ring with the same outer edges can have the same bounding box. Inspect the paths and fill rules to check openings.
Can I use the client rectangle as a viewBox? Not directly. Convert the relevant geometry into the root SVG's user coordinates first. Screen position and CSS-pixel dimensions should not be copied into a source-coordinate rectangle.
Sources
- MDN getBBox — Defines local SVG bounds and the optional measurement controls.
- MDN getBoundingClientRect — Explains viewport-relative rectangles and scrolling.
- SVG bounding-box definitions — Distinguishes object, stroke, and decorated geometry bounds.
- MDN getScreenCTM — Documents the matrix used to connect SVG and viewport coordinates.
More from the blog

SVG Motion Paths: Move a Custom Icon Along a Curve
Move SVG artwork along a curve with animateMotion and mpath. Fix off-center icons, choose automatic rotation, and provide a static version for reduced motion.

SVG preserveAspectRatio: Fit Artwork Without Stretching
Choose meet, slice, or none for SVG fitting. Test alignment, clipping, and empty canvas with one emblem, and separate SVG mapping from CSS object-fit.