Skip to content

bug: SVG JSX sometimes don't get rendered as the correct DOM elements #6257

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
3 tasks done
james-nash opened this issue May 16, 2025 · 1 comment
Open
3 tasks done
Labels
ionitron: needs reproduction This PR or Issue does not have a reproduction case URL

Comments

@james-nash
Copy link

Prerequisites

Stencil Version

4.30.0

Current Behavior

A functional component rendering an <svg> element was rendering it and its children incorrectly as HTML elements (presumably via document.createElement('svg');) rather than SVG elements (i.e. via document.createElementNS('http://www.w3.org/2000/svg', 'svg');). Therefore, the elements are in the DOM, but since they're not "real" SVG elements, the browser ignores them and doesn't display the SVG as expected.

After much trial and error, we eventually found a solution: We were importing our functional component's module with a .js extension (i.e. import FooComponent from '../Foo.js';). When we removed that (i.e. import FooComponent from '../Foo';), it rendered as a "genuine" SVG. So, in both cases, functional compononent is successfully imported and used. It's just that when the .js extension is there it doesn't render SVG elements correctly.

We use other functional components that just render HTML elements and those work fine when imported with .js extensions.

Expected Behavior

<svg> in a functional component's JSX should render the correct DOM elements, regardless of how they are imported.

System Info

System: node 20.19.0
    Platform: darwin (24.4.0)
   CPU Model: Apple M2 Pro (12 cpus)
    Compiler: /Users/james.nash/code/investec-design-system/node_modules/@stencil/core/compiler/stencil.js
       Build: 1745512551
     Stencil: 4.30.0 🌺
  TypeScript: 5.5.4
      Rollup: 4.34.9
      Parse5: 7.2.1
      jQuery: 4.0.0-pre
      Terser: 5.37.0


Viewed in current versions of Firefox and Chrome - same result in both.

Steps to Reproduce

We have a monorepo setup (for a design system) and one of the packages within it contains Stencil web components (which a built using the dist-custom-elements output target). We have a number of Stencil functional components for stuff shared by multiple web components.

One of the functional components renders an <svg> element (it's used for displaying icons). That component's code is as follows:

// Icon.tsx
// ...

export const IdsIcon: FunctionalComponent<IdsIconProps> = ({
	name,
	size,
	appearance,
	alt,
	class: className,
	titleId,
	...rest
}) => {
	const titleElementId = genId(titleId);

	return (
		<svg
			width="16"
			height="16"
			class={getIconClassNames({ size }, className)}
			role="img"
			aria-hidden={alt === '' ? 'true' : undefined}
			aria-labelledby={alt === '' ? undefined : titleElementId}
			{...rest}
		>
			{alt !== '' && <title id={titleElementId}>{alt || name}</title>}
			<use href={getIconGlyphUrl(name, appearance, getIdsAssetFilePath)} />
		</svg>
	);
};

It's then imported into a web component and used in its render() method:

// Card.tsx
// ...
 
import { IdsIcon } from '../../html/Icon/Icon.js';

// ...

@Component({
	tag: 'ids-card',
	shadow: true,
})
export class IdsCard
	implements Omit<IdsCardCommonProps<never>, 'children'>
{
	// ...

	render() {
		return (
			{/* ... */}
			<IdsIcon name="cross" />
			{/* ... */}
		);
	}
}

The result (previewed here in Storybook) is:

screenshot of a fake SVG element not visibly rendering anything

...as you can see:

  • There is an <svg> element in the DOM, but...
  • Nothing appears visibly (it's supposed to display an X icon)
  • As shown in the console, that <svg> DOM element is an instance of HTMLElement, not SVGElement, as it's supposed to be.

And, more or less by fluke, we discovered when we change the import statement to not use the .js extension:

-import { IdsIcon } from '../../html/Icon/Icon.js';
+import { IdsIcon } from '../../html/Icon/Icon';

...it suddenly works as expected:

SVG rendering correctly

Code Reproduction URL

n/a

Additional Information

Our code is not (yet) open source, so I'm afraid I can't share it.

I did try to make a simplified reproduction and, weirdly, I couldn't get it to fail. The SVG rendered correctly regardless of whether I imported it with a .js extension or not. I was using the same Stencil version and the same output target config.

I appreciate this will be difficult (impossible?) to investigate, but still wanted to raise it for awareness. Perhaps others have encountered similar issues and some of what I've shared here will provide some useful clues 🤞

Whatever this is, it certainly smells like a bug, since I wouldn't expect Stencil to render JSX differently just because a .js extension on an import.

@james-nash james-nash changed the title bug: bug: SVG JSX sometimes don't get rendered as the correct DOM elements May 27, 2025
@christian-bromann christian-bromann added the ionitron: needs reproduction This PR or Issue does not have a reproduction case URL label May 30, 2025
@christian-bromann
Copy link
Member

Thanks for raising awareness about this problem.

I appreciate this will be difficult (impossible?) to investigate

Unfortunately there is not much we can do until we can work on something that allows us to reproduce the issue. Let me know if you manage to put together a reproduction case. I am happy to take a look then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
ionitron: needs reproduction This PR or Issue does not have a reproduction case URL
Projects
None yet
Development

No branches or pull requests

2 participants