Reading SVG’s path: Straight Lines and Sharp Corners
<path> is routinely cited as the hardest part of SVG, but the reputation is only partly deserved. The syntax looks alien next to the friendly, parameter-driven rect or circle, and yet the underlying logic is simple: every shape you’ve drawn with those convenience elements is really just a path with a fixed set of drawing instructions hidden away. Where rect knows it must trace four sides, path knows nothing until you tell it, command by command, where to go.
This guide covers the first half of that command vocabulary: the ones that produce straight, angular lines. These let you replace line, polyline, and polygon with a single, more flexible element — and combine multiple disconnected shapes into one. Bending lines with curves and arcs will follow in a second installment.
The examples below assume basic SVG familiarity — viewBox, coordinates, and the simple shape elements — plus a working knowledge of the <text> element if you want to trace every line in the demos. All code is framework-agnostic vanilla JavaScript. For a viewBox whose origin sits at the top-left corner, each example plots on top of a labeled grid like this one:
See the Pen [SVG Viewbox Grid Visual [forked]](https://codepen.io/smashingmag/pen/MYwEdVN) by Myriam.
Starting Points With M
Every path begins with the M command, which moves the pen to a starting coordinate. It takes two arguments — an x and a y position — and draws nothing. A path made up of only an M command is empty, which is why cleaning up SVG files routinely deletes them.
const uselessPathCommand = `M${start.x} ${start.y}`;
The Straight-Line Commands: L, H, V
Once the pen is placed, three commands draw a line from the current point to a specified new point:
Ltakes two arguments, thexandycoordinates of the destination.Htakes one argument, anxposition. Theyvalue is inherited from the current point, producing a horizontal line.Valso takes one argument, ayposition. Thexvalue is inherited, producing a vertical line.
That inheritance is the key efficiency gain. A diagonal line needs the full coordinate pair — M10 10 L100 100. But a horizontal line can omit the redundant y value: M10 55 H100 asks SVG to reuse the y from the M command. The V command works the same way with the x coordinate.
const pathCommandL = `M${start.x} ${start.y} L${end.x} ${end.y}`;
const pathCommandH = `M${start.x} ${start.y} H${end.x}`;
const pathCommandV = `M${start.x} ${start.y} V${end.y}`;
Rendered over the grid, the three commands behave exactly as expected: the red path moves diagonally, the blue path stays perfectly horizontal, and the green path runs vertically.
See the Pen [Simple Lines with path [forked]](https://codepen.io/smashingmag/pen/azOLrjZ) by Myriam.
Compare this path notation to the equivalent <line> element:
- The
pathversion is more compact. - The
lineversion is self-describing, while the path string reads as gibberish without context.
That trade-off — brevity versus legibility — is central to deciding when path is worth the added syntax.
<path d="M 10 55 H 100" />
<line x1="10" y1="55" x2="100" y2="55" />
Closing Shapes With Z
Like <polygon>, a path can be explicitly closed by drawing a straight line back to its starting point. The Z command does this with no arguments — it always connects the current point to the origin of the current path segment.
const polyline2Points = `M${start.x} ${start.y} L${p1.x} ${p1.y} L${p2.x} ${p2.y}`;
const polygon2Points = `M${start.x} ${start.y} L${p1.x} ${p1.y} L${p2.x} ${p2.y} Z`;
The difference between an open polyline-style path and a closed polygon-style one is a single trailing Z. This lets a single path element alternate between the two behaviors, as a repeating triangle pattern demonstrates:
See the Pen [Alternating Triangles [forked]](https://codepen.io/smashingmag/pen/emNGaPm) by Myriam.
When comparing path with dedicated polygon and polyline elements, the semantic advantage of the named tags is thin. Most readers will parse line intuitively, but “polygon” and especially “polyline” are not common vocabulary. To most developers, both the named element and the raw path string carry the same level of meaningful information.
<path d="M0 0 L86.6 50 L0 100 Z" />
<polygon points="0,0 86.6,50 0,100" />
<path d="M0 0 L86.6 50 L0 100" />
<polyline points="0,0 86.6,50 0,100" />
Relative Commands: m, l, h, v
Every line command has a lowercase counterpart that is relative to the current pen position. Instead of declaring an absolute x, you provide a dx: the number of units to move from wherever the pen currently sits. The mapping is straightforward: m, l, h, and v correspond to their uppercase relatives.
Consider three distinct command strings:
const lines = [
{ d: `M10 10 L 10 30 L 30 30`, color: "var(--_red)" },
{ d: `M40 10 l 0 20 l 20 0`, color: "var(--_blue)" },
{ d: `M70 10 l 0 20 L 90 30`, color: "var(--_green)" }
];
Reading them in isolation, the relative versions can be more revealing. A 0 in the horizontal position of a relative command instantly signals that the line will not move sideways. A repeated value like 20 hints at regular spacing or shape dimensions. Absolute coordinates carry no such clues — a string of large, arbitrary numbers says nothing about the geometry without a calculator. Yet all three paths below trace identical shapes, merely shifted to different positions on the grid.
See the Pen [SVG Compound Paths [forked]](https://codepen.io/smashingmag/pen/vEOewQp) by Myriam.
Neither syntax is inherently superior. Relative values simplify patterns where the same offset repeats; absolute values win when coordinates map cleanly to known positions. There is also a more efficient option for generating repeated shapes than hand-writing either form: store the gap and shape size in variables, then loop over an index to compute each start point.
Compound Paths: Multiple M Commands
The M command has a second, less obvious role. Since it only moves the pen without drawing, a single path can contain many M commands to jump between disconnected subpaths. These are compound paths, and they collapse many styled shapes into one element.
<path d="M0 0 H110 M0 10 H110 M0 20 H110 M0 30 H110 M0 0 V45 M10 0 V45 M20 0 V45 M30 0 V45 M40 0 V45 M50 0 V45 M60 0 V45 M70 0 V45 M80 0 V45 M90 0 V45" stroke="currentColor" stroke-width="0.2" fill="none"></path>
The benefit is visible in the grid that underlies those examples. Early versions drew the grid with 14 separate <line> elements. The final version squeezes the entire grid into a single <path> inside the .grid group, chaining several M commands together.
Whenever multiple shapes share styling and don’t require separate user interactions, compounding them into one path produces shorter, cleaner markup. The trade-off is that the resulting d string becomes harder to inspect by eye — but the command vocabulary itself stays identical.
With line commands mastered, you can now replace line, polyline, and polygon with path, and merge them arbitrarily. The next step in the language of paths goes beyond straight edges, adding curved segments and arcs that let a single element trace circles, ellipses, and free-flowing contours. That’s the subject of the companion piece on curves.



