Adding custom icons to your TinyMCE 5 Plugins
The TinyMCE 5 rich text editor launched in early 2019, and with it an improved interface to help developers build custom plugins. Firstly – read this to learn how to write a custom plugin for Tiny 5.
One question I get asked about a bit is custom icons – and what the “best” way to approach their implementation.
In the Tiny 5 documentation, one of the Advanced Topics discusses creating an Icon Pack – but what if you just need one icon? An icon pack feels a bit over-the-top if you just need one or two icons for your plugin.
Where does that leave us?
There are two ways to approach this:
Create an external SVG file for your icons, and refer to that in your plugin
Embed your SVG icons in your plugin code
Which way to go? Your choice.
If you use an external SVG file:
Path data is all in one place and in a native (and can be source formatted, highlighted and read in your IDE)
Adds an extra HTTP request
If you use embedded SVG data:
Plugin is all-inclusive (no extra file needed)
One less HTTP request (and Tiny does make plenty of those on its own
Harder to read and scan path data
However, if you need to change your SVG, you also need to recompile your plugin file
If you’re looking at an external and referenced SVG file, check out the first blog post about writing custom plugins, and how to use custom icons in an SVG file.
Regardless of whether you’re going external or embedded, the approach now is the same.
Being beautifully scalable, SVGs are awesome. But they’re also a point of confusion if you’re not familiar with their structure. Creating your icons (and having them appear as expected) in your Tiny plugin buttons requires two key steps.
Define the width/height of your SVG element
For any icon you want to appear in a Tiny toolbar, make sure you set the size using the width and height attributes to be 24 x 24.
<svg width="24" height="24"> ... </svg>
This will instruct Tiny to render the icon as a 24 x 24 square.
Correctly define your viewBox for your path data
Each icon you include will (most likely) need to have its own unique viewBox defined on the SVG element.
<svg width="24" height="24" viewBox="0 0 576 512"> ... </svg>
viewBox defines the actual box that the SVG data will fit in – maybe it is 64 x 64, maybe it is 576 x 512. It doesn’t matter – as long as you define it – because the width and height you set previously ensures it will be rendered as a 24 x 24 square.
The coordinates are:
So "0 0 576 512" means its container starts at 0,0, and is 576 pixels wide and 512 pixels high. All of the path data for this icon fits within this size container.
If you’re using an icon library, you’ll be able to see the viewBox for specific icons by looking at the source SVG data.
If you’re building your own SVG icons, then you know what you’re doing, and just need to ensure you’re setting the viewBox based on your artwork.
This also means that each icon could have its own viewBox set – and be different to every other icon. And that’s OK too.
The basic thing we’re looking at here is ensuring SVGs are 24 x 24, and the viewBox is defined, so that the innards of your SVG are rendered correctly within that 24 x 24 port.
Note that if you're using an external SVG (that you host), include your viewBox in the SVG file. Your Tiny addIcon call can the specify the width/height. The initial plugin creation post sets it up this way.
Let’s say you’re embedding an icon in your plugin.js file. You’ll simply call the same editor.ui.registry.addIcon
/* * Add a custom icon to TinyMCE */ editor.ui.registry.addIcon('bubbles', '<svg width="24" height="24" viewBox="0 0 576 512"><path d="M240 64c-25.333 0-49.791 3.975-72.693 11.814-21.462 7.347-40.557 17.718-56.751 30.823-30.022 24.295-46.556 55.401-46.556 87.587 0 17.995 5.033 35.474 14.96 51.949 10.343 17.17 25.949 32.897 45.13 45.479 15.22 9.984 25.468 25.976 28.181 43.975 0.451 2.995 0.815 6.003 1.090 9.016 1.361-1.26 2.712-2.557 4.057-3.897 12.069-12.020 28.344-18.656 45.161-18.656 2.674 0 5.359 0.168 8.047 0.509 9.68 1.226 19.562 1.848 29.374 1.848 25.333 0 49.79-3.974 72.692-11.814 21.463-7.346 40.558-17.717 56.752-30.822 30.023-24.295 46.556-55.401 46.556-87.587s-16.533-63.291-46.556-87.587c-16.194-13.106-35.289-23.476-56.752-30.823-22.902-7.839-47.359-11.814-72.692-11.814zM240 0v0c132.548 0 240 86.957 240 194.224s-107.452 194.224-240 194.224c-12.729 0-25.223-0.81-37.417-2.355-51.553 51.347-111.086 60.554-170.583 61.907v-12.567c32.126-15.677 58-44.233 58-76.867 0-4.553-0.356-9.024-1.015-13.397-54.279-35.607-88.985-89.994-88.985-150.945 0-107.267 107.452-194.224 240-194.224zM498 435.343c0 27.971 18.157 52.449 46 65.886v10.771c-51.563-1.159-98.893-9.051-143.571-53.063-10.57 1.325-21.397 2.020-32.429 2.020-47.735 0-91.704-12.879-126.807-34.52 72.337-0.253 140.63-23.427 192.417-65.336 26.104-21.126 46.697-45.913 61.207-73.674 15.383-29.433 23.183-60.791 23.183-93.203 0-5.224-0.225-10.418-0.629-15.584 36.285 29.967 58.629 70.811 58.629 115.838 0 52.244-30.079 98.861-77.12 129.382-0.571 3.748-0.88 7.58-0.88 11.483z"></path></svg>');
And it is as easy as that.
Given your icons are most likely very simple, the size of your plugin.js file shouldn’t balloon – for an example, uncompressed, the “helloworld” plugin + external SVG file was 2.75kb, where as the plugin with an embedded icon was 2.51kb. Not that you’re really going to notice a difference with sizes like this. If you’re developing a suite of plugins, then an external icon file may make more manageable sense for your team, but if you just need one icon, embed it and keep it simple.
What if my icon appears too big?
Your icon could appear too big (compared to the other icons in the toolbars) if your icon reaches the hard limits of your viewBox dimensions. And that's OK if it does - but it does look a bit weird.
You can overcome this by tweaking the width and height of the svg element to suit your icon - maybe it needs a smidge less height, maybe a smidge less width. If you have a very square icon (and it appears too large) maybe change its width and height to be 24 x 22 respectively. That two pixel difference means your SVG renders in a slightly smaller box (but still shows all of its viewBox data).
There's no magic formula here - just have a play, and know that you can adjust the width and height if you need to case-by-case for your icons.
Can I use coloured icons?
There is one other thing to remember… just because you can, doesn’t mean you should.
If you include a complex and colourful SVG icon (such as a branded icon), Tiny will display it. So yes, you could use coloured icons. However, it is also important to consider accessibility implications.
If you use simple single-colour icons, Tiny is smart enough to flip their colour based on the skin being used. If the skin’s back colour is dark, the icons will be rendered light, allowing for good contrast between the background and foreground elements.
If you use a complex and coloured SVG, Tiny will always display your SVG exactly as you designed it. If the skin of the editor becomes dark, and your icon is dark, then it may become very challenging to see.
Keep it simple (and keep a consistent UX through consistent UI elements) and try to keep your SVG icons simple, clean and single colour. You’ll get the greatest amount of flexibility for your icon – and allow your users to tinker with their editor appearance and trust that Tiny’s infrastructure will handle icon contrast accordingly.
When building your plugins, remember those two important settings: width and height for controlling the final rendered size, and viewBox for dictating the actual scale of your SVG data. Set these up correctly, and you are all set for using custom icons in your TinyMCE plugins.