Skip to content

fix(runtime): map camelCase SVG props to kebab-case attributes#6776

Closed
Arul1998 wants to merge 2 commits into
stenciljs:mainfrom
Arul1998:feat/svg-camel-case-props
Closed

fix(runtime): map camelCase SVG props to kebab-case attributes#6776
Arul1998 wants to merge 2 commits into
stenciljs:mainfrom
Arul1998:feat/svg-camel-case-props

Conversation

@Arul1998

Copy link
Copy Markdown
Contributor

SVG attribute matching is case-sensitive, so camelCase props spread onto SVG elements (e.g. strokeWidth) were set as-is and silently ignored by the renderer. Map them to their kebab-case attribute names like React does, leaving SVG's genuinely camelCase attributes (viewBox, preserveAspectRatio, etc.), xml/xmlns prefixed names, and xlink handling unchanged. The mapping is gated behind the svg build conditional.

Fixes #5674

What is the current behavior?

GitHub Issue Number: #5674

camelCase SVG props are set verbatim as attributes. Since SVG attribute matching is case-sensitive, an attribute named strokeWidth does not match stroke-width and the styling is silently ignored — no warning or error. This mainly bites when spreading an object of props onto an SVG element (React-style prop objects, shared prop factories, etc.):

const circleProps = { cx: 15, cy: 5, r: 3, stroke: 'green', strokeWidth: 3 };

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 10">
  <circle {...circleProps} />
</svg>

renders <circle ... strokeWidth="3">, which has no visual effect.

What is the new behavior?

In an SVG context, camelCase prop names are mapped to their kebab-case SVG attribute names (strokeWidthstroke-width, fillOpacityfill-opacity, …) for setting, updating, and removal — matching React's behavior for SVG props.

Deliberately excluded from the mapping:

  • SVG attributes that are genuinely camelCase per the SVG specification (viewBox, preserveAspectRatio, gradientTransform, baseFrequency, etc.) — passed through unchanged via an explicit exception list
  • tabIndex and crossOrigin, which map to all-lowercase attribute names rather than kebab-case
  • xml/xmlns prefixed names — left unmodified
  • existing xlink handling — the mapping runs after it, so xlinkHref continues to work as before

The mapping only applies in SVG contexts (isSvg), is gated behind the svg build conditional so it is tree-shaken from applications that don't render SVG, and non-SVG elements are unaffected.

The kebab-case prop names Stencil's JSX typings declare today (stroke-width={3}) continue to work exactly as before — this is purely additive.

Documentation

N/A — behavior now matches what the JSX allows; happy to add a docs note to the SVG guide in stenciljs/site if desired.

Does this introduce a breaking change?

  • Yes
  • No

Previously, camelCase names in SVG contexts produced attributes that SVG ignores, so no working code depended on the old behavior.

Testing

  • Six new unit tests in set-accessor.spec.ts: setting, updating, and removing a camelCase prop as its kebab-case attribute; camelCase-in-spec attributes (viewBox, preserveAspectRatio) preserved; tabIndextabindex; and no mapping outside SVG contexts. Full set-accessor spec: 132/132 passing.
  • Full runtime jest suite run locally: 655/655 across 63 suites — no regressions, including the existing SVG patching and xlink tests.
  • Manual end-to-end: built a component using the exact reproduction from feat: support prop casing for SVG attributes #5674 against this branch and inspected the DOM in a browser — the circle renders <circle cx="15" cy="5" r="3" stroke="green" stroke-width="3">, computed style reports stroke-width: 3px, and no strokeWidth attribute is present.

Other information

The exception list mirrors the camelCase attribute names from the SVG specification (the same set React preserves). It lives behind the BUILD.svg conditional and a @__PURE__ annotation, so it adds no bytes to builds without SVG rendering.

SVG attribute matching is case-sensitive, so camelCase props spread onto SVG elements (e.g. strokeWidth) were set as-is and silently ignored by the renderer. Map them to their kebab-case attribute names like React does, leaving SVG's genuinely camelCase attributes (viewBox, preserveAspectRatio, etc.), xml/xmlns prefixed names, and xlink handling unchanged. The mapping is gated behind the svg build conditional.

Fixes stenciljs#5674
@Arul1998 Arul1998 requested a review from a team as a code owner July 16, 2026 11:28

@johnjenkins johnjenkins left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️
Can you add a spec test to src/runtime/test/svg-element.spec.tsx 🙏

@johnjenkins johnjenkins changed the title feat(runtime): map camelCase SVG props to kebab-case attributes fix(runtime): map camelCase SVG props to kebab-case attributes Jul 16, 2026
@Arul1998

Copy link
Copy Markdown
Contributor Author

Added integration specs to svg-element.spec.tsx one covering the spread-props reproduction from the issue rendered through the vdom, and one covering update/removal of a mapped attribute via a @prop. Thanks for the review!

@Arul1998 Arul1998 requested a review from johnjenkins July 16, 2026 17:30
@johnjenkins

johnjenkins commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

hey @Arul1998 - again, thanks for looking into this.
Having looked at the solution and thinking about it some-more, I don't think I'm going to merge this.

  1. Sorry about that
  2. Sorry for adding the 'needs help' tag on the original issue

Sometimes you just don't think things through until you're forced to think them through.

The reasons:

  • if we look at the original issue raised, it's a "nice to have" with 0 upvotes and 0 comments. This signals to me that it really isn't an issue developers are experiencing.
  • Naturally, I'm quite protective around adding anything to the runtime - especially something that a lot of users will 'pay' for. Anyone using <svg> will pay a ~1kb (600b gzip) tax ... for something that no one else has complained about or asked for.
  • The list of attributes is just another list that requires maintenance as attributes drift in / out of the spec

Ultimately, users can achieve a spread on svg children via {'stroke-width': 3} .. and I'm going to assume that's "fine" for most users.

Again - thank you so much for supporting Stencil with your time 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: support prop casing for SVG attributes

2 participants