SVG is surprisingly complex because of the way SVG's are coded and WordPress's handling of the SVG format. It's less a Gutenberg/Block Editor issue and more of a 'WordPress doesn't like SVG' type issue. The reason WP doesn't natively support SVG is because an SVG file is just plain ole' XML - and as such, anything could be passed in the XML code leading to some pretty bad vulnerabilities.
However, Gutenberg DOES enable you to take advantage of SVG functionality and provides components necessary for you to do so in just about any variety you want. When I ran up against a similar problem in creating a list of custom SVG icons (there were 30+ options) a client could configure and display across different posts, rather than returning the SVG code itself what you want to use is a combination of:
Icon from @wordpress/components
SVG from @wordpress/primitives
and Path from @wordpress/primitives
I used Gutenberg's code for handling it's WP's SVG icon as a reference.
Here is my specific code implementation for reference:
(In this case, I wanted an automated way of pulling in pathCode because I had SVG's with complex path patterns). The reason it's good to import the primitives here is that we can import just about any specific individual Icon primitive we need to construct our custom SVG implementation.
File: custom-svg-icon.js
/**
* WordPress Dependencies
*/
import { SVG, Path } from '@wordpress/primitives';
/**
* CustomSVGIcon takes in properties necessary to render an SVG component.
* 'Global' or potentially changeable attributes, such as viewBox are declared here, while
* other components that need a different render path pass their d= value into the component.
* Currently, .map() does not seem to work on tag attributes for this specific implementation.
* @param path the custom d= value for an SVG's path parameter.
* @returns {JSX.Element} Custom icon component that can be passed to WP's Icon component.
* @constructor
*/
const CustomSVGIcon = ( { pathCode } ) => {
return (
<SVG
viewBox={ '0 0 39.18 48' }
preserveAspectRatio={ 'xMidYMid meet' }
xmlns={ 'http://www.w3.org/2000/svg' }
>
<Path fill={ 'var(--wp--preset--color--primary)' }
d={ 'M0 47.98v-48h.28 38.9v48H0z' } />
<Path fill={ 'var(--wp--preset--color--white)' }
d={ pathCode } />
</SVG>
);
}
export default ( CustomSVGIcon );
I created an icon-data.js file to control and add different parameters, importing CustomSVGIcon.
/**
* Internal Dependencies
* @type {[{code: string, name: string}, {code: string, name: string}, {code: string, name: string}, {code: string, name: string}]}
*/
import CustomSVGIcon from './custom-svg-icon';
export const letters = [
{
name: 'Letter A',
code: 'M34.09 35.68l-10.91-6.67c0 .28 0 .52.06.75l.53 5.27.46 4.79.31 3.14c0 .2 0 .26-.22.25h-2c-.19 0-.24-.07-.26-.24l-.4-4.2c0-.19-.09-.26-.29-.26h-4.05c-.19 0-.25.08-.27.25l-.35 3.69a5.73 5.73 0 0 1-.07.58c0 .07-.08.17-.13.17h-2.32c0-.4.06-.76.1-1.13l.32-3.33.33-3.27.32-3.28.37-3.78.32-3.22.32-3.33.32-3.24.32-3.24.17-1.74c0-.16.06-.22.22-.22h4.15c.12 0 .19 0 .2.18l.34 3.51.38 3.75.31 3.26.19 1.82a.34.34 0 0 0 .16.24l8.58 5.26.12.05a1.8 1.8 0 0 0 0-.22V6.91c0-.23-.08-.26-.28-.26H7.11c-.38 0-.38 0-.38.37v25c0 .38 0 .38-.38.38H4.64c-.17 0-.26 0-.24-.24a.75.75 0 0 0 0-.15V4.71c0-.41 0-.37.37-.37h29c.36 0 .36 0 .36.36v31zM19.41 16.49h0v.32l-.27 2.67-.38 3.65-.36 3.63-.38 3.73-.33 3.16-.21 2.19c0 .16 0 .24.2.24h3.53c.18 0 .23-.08.21-.24l-.12-1.25-.43-4.17-.41-4.17-.53-5.13z'
},
{
name: 'Letter B',
code: 'M34.07 35.67l-10.18-6.22v9.62a4 4 0 0 1-3.39 4 6.47 6.47 0 0 1-1.08.09h-4.36V13.44h.23 4.39a4.29 4.29 0 0 1 3.79 2.19 3.41 3.41 0 0 1 .42 1.71v5.33a3.51 3.51 0 0 1-1.23 2.7l-.34.32v.07l9.44 5.77v-.29q0-12.13 0-24.27c0-.31 0-.31-.34-.31H7.02c-.25 0-.32.07-.32.32q0 12.54 0 25.06a.69.69 0 0 1 0 .25c0 .07-.11.17-.17.17h-2c-.06 0-.14-.1-.17-.17a.54.54 0 0 1 0-.22V4.74c0-.47 0-.4.41-.4h28.92c.36 0 .36 0 .36.35v31zM17.5 25.55v15.07a1.21 1.21 0 0 1 0 .19c0 .18.06.23.23.23h1.56a3.6 3.6 0 0 0 .73-.06 1.68 1.68 0 0 0 1.39-1.55v-10.7a1.36 1.36 0 0 0-.69-1.21l-1.77-1.09zM21.41 20v-2.69a1.48 1.48 0 0 0-.48-1.12 1.93 1.93 0 0 0-1.38-.55h-1.81c-.2 0-.24.08-.24.26v6.77a.43.43 0 0 0 .15.29l2.19 1.34a.3.3 0 0 0 .22 0 1.72 1.72 0 0 0 1.34-1.6l.01-2.7z'
},
{
name: 'Letter C',
code: 'M24.01 19.51h-2.55v-.29-1.83a1.73 1.73 0 0 0-1.3-1.76 2.26 2.26 0 0 0-2.39.64 1.37 1.37 0 0 0-.33.88v5.44c0 .16.08.23.2.3l8.38 5.12 5.51 3.37.27.15v-.22q0-12.15 0-24.3c0-.25-.06-.32-.31-.32H7.04c-.25 0-.32.06-.32.32q0 12.55 0 25.11v.36h-.3-1.7c-.23 0-.35 0-.3-.3V4.36h29.65v31.3L17.42 25.49v14.15a1.75 1.75 0 0 0 1.74 1.89 2.22 2.22 0 0 0 1.53-.34 1.61 1.61 0 0 0 .75-1.4v-2.14c0-.2.06-.28.27-.27h2.05.24v.27 1.78a3.55 3.55 0 0 1-1.43 3 5 5 0 0 1-3.8 1 4.68 4.68 0 0 1-2.73-1.24 3.55 3.55 0 0 1-1.2-2.71V22.31v-4.74a3.85 3.85 0 0 1 2.43-3.75 5 5 0 0 1 6 1.35 3.25 3.25 0 0 1 .7 2l.04 2.34z'
},
{
name: 'Letter S',
code: 'M14.4 32.54h2.43v.34 6.21a1.84 1.84 0 0 0 1.37 2 2.93 2.93 0 0 0 2.92-.4 1.54 1.54 0 0 0 .63-1.3q0-4.73 0-9.47v-1.28a.4.4 0 0 0-.24-.41l-6.94-4.24a.5.5 0 0 1-.26-.49V18a3.68 3.68 0 0 1 1.31-2.91 5.17 5.17 0 0 1 3.4-1.29 5.29 5.29 0 0 1 3.91 1.38 3.54 3.54 0 0 1 1.22 2.75v4.26h-.23-2c-.19 0-.25-.06-.25-.25v-3.8a1.76 1.76 0 0 0-1.2-1.84 2.94 2.94 0 0 0-3.15.4 1.48 1.48 0 0 0-.57 1.18q0 2.22 0 4.44a.37.37 0 0 0 .19.35l14.6 8.92.18.1v-.22V7c0-.27-.12-.26-.31-.26H7.08c-.39 0-.39 0-.39.39v25.43H4.4V4.45h29.7v31.36l-9.87-6v.34 9.1a3.81 3.81 0 0 1-1.56 3.19 5.6 5.6 0 0 1-7.22-.43 3.51 3.51 0 0 1-1.07-2.5v-6.75a1.37 1.37 0 0 1 .02-.22z'
}
];
const icon = letters[3]['code'];
export const blockIconCode = (
<CustomSVGIcon pathCode={ icon } />
)
Then I compiled them both together as my custom component:
File: get-icon.js
/**
* WordPress Dependencies
*/
import Icon from '@wordpress/icons'
/**
* Internal Dependencies
*/
import CustomSVGIcon from './custom-svg-icon';
import { blockIconCode, letters } from './icon-data';
/**
* Take the list.
* Render items in the list as a <CustomSVGIcon />
* Return values as options the block editor can use.
* Save values to render on main page.
*/
// The Block Icon.
export const blockIcon = () => <Icon icon={ blockIconCode } />
export const letterA = (
<CustomSVGIcon pathCode={ letters[0]['code'] } />
);
And then finally, you can put this all together in your own implementations as you need, using Icon imported from @wordpress/component again:
/**
* WordPress Dependencies
*/
import { Icon } from '@wordpress/components';
/**
* Internal Dependencies
*/
import { letterA } from './utils/get-icon';
import './editor.scss';
export function IconEdit (props) {
// console.log used for testing props passed as wanted/intended.
// console.log(props)
return (
<div>
<Icon icon={ letterA } size={128}/>
</div>
)
}
export default ( IconEdit );
This is a somewhat complex implementation, but thoroughly goes through just about every parameter you need for SVG - you could drastically simplify the steps (I had to stack two distinct paths in order to do color appropriately for these icons, so had to stack a path / component within an to then ultimately pass to ), and in my ultimate iteration I ended up using a class to assign the letter names etc so I wouldn't have to redo the code again, but you could also just list them all out.
//TODO Here is the Icon component code in github.
Gutenberg Code References I used to build my implementation:
Here is how gutenberg constructs Icons for stories
Gutenberg's actual implementation of the Icon's you see by default in WP: The Icon Package Directory
The code dir for each individual icon
The actual icon function
The index export file of all icons.