In-line SVG’s in Next Experience Components

When creating components that show complex graphics and paths, it’s common that you will want to an SVG (Scalable Vector Graphic). SVG’s can be included in HTML in multiple ways, including with an img tag via its src attribute. For example:

<img src="/my-svg-file.svg"/>

However, if you want to target the components of an SVG with CSS or JavaScript, then you’ll need to use an use an in-line SVG. For example:

<svg class="mySvgClass" xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
    <circle id="back" cx="40" cy="40" r="40" fill="red" />
</svg>

With the above svg code on our page, in addition to gaining access to the elements of the SVG with JavaScript just like any other HTML element, we can also use CSS to style the elements of our SVG, or even to animate them! For example:

.mySvgClass circle {
    fill: yellow;
}

However, Next Experience components are created in JSX, which is kind of like a combination of HTML and JavaScript. You might try to put an SVG directly in-line in your view in the JSX like so:

const view = (state, { updateState }) => {
    return (
        <div>
            <svg class="mySvgClass" xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40">
                <circle id="back" cx="40" cy="40" r="40" fill="red" />
            </svg>
        </div>
    )
};

You’ll find when you try to do that though, you’ll get a message the same, or similar to the below when you run your component:

An error message when trying to in-line an SVG inside your JSX code.

Instead of putting your SVG directly in-line with you JSX, you can create a helper function to create an SVG element and add it to your page. For example the below _renderSvg.js will do this:

import {createElementFromNode} from '@servicenow/ui-renderer-snabbdom';

export default ({className, width, height, innerSVG}) => {
	const wrapper = document.createElement('SPAN');
	wrapper.innerHTML = `<svg class="${className}" xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">${innerSVG}</svg>`;
	const vnode = createElementFromNode(wrapper);
	return vnode.children[0];
};

Then, anywhere you want to include an in-line SVG you can call this function to create an SVG for you. To continue on from the previous example view, we can leverage _renderSvg.js like so:

import renderSVG from '../_rendersvg';

...

const mySvg = renderSVG ({
		className: 'mySvgClass',
		width: 40,
		height: 40,
		innerSVG: '<circle id="back" cx="40" cy="40" r="40" fill="red" />'
	});

const view = (state, { updateState }) => {
    return (
        <div>
            {mySvg}
        </div>
    )
};

After doing so, the errors will no longer appear, and our SVG will appear on the page as expected.

An SVG appearing on a page