Replace Discourse's default SVG icons with custom icons in a theme

As of Discourse 2.3.0.beta6 (and thanks to @pmusaraj) you can now add custom SVG icons and override the defaults within a theme. You can replace a single icon, or the entire set. Here’s how:

Create an SVG Spritesheet

To get started, you must combine your SVG icons into a single spritesheet. If you have a few icons it’s easy enough to do this manually. If you have many icons, there are ways to automate this. If you’re not already familiar with some of the Grunt tools to combine SVGs, the simplest command line tool I’ve found for this is svg-sprite-generator - npm.

The spritesheet should be saved as an SVG file, and is structured like this:

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="" style="display: none;">

    <symbol id="mytheme-icon-1">
      SVG code here, if you view the source of your SVG file
      this is typically everything between the <svg> tags 
      (but not the SVG tag itself, that's replaced by <symbol> above) 

    <symbol id="mytheme-icon-2">
      <!-- SVG code here -->

  • Be sure to add a custom ID to each symbol in the spritesheet. It’s probably helpful for your sanity to prefix your IDs with your theme name mytheme-icon-link.

  • Be on the lookout for style collisions within your SVGs. For example, SVGs will often have an inline style like .st0{fill:#FF0000;} defined. If you have multiple SVGs using the same classes this can cause issues (to fix these issues, edit the classes to be unique to each icon).

Once your spritesheet is built, you need to add theme SVG file to your theme’s asset folder and reference it as “icons-sprite” in the about.json file. For an SVG sprite called my-icons.svg, for example, your assets.json should include this:

"assets": {
   "icons-sprite": "/assets/my-icons.svg"

If you are uploading the SVG sprite through the Discourse UI, make sure you use “icons-sprite” as the SCSS var name for the upload.

Overriding default icons

Now that your spritesheet is set, you need to tell Discourse which icons to replace. This is how you do it (you can add this to your header.html file)

<script type="text/discourse-plugin" version="0.8">
  api.replaceIcon('bars', 'mytheme-icon-bars');
  api.replaceIcon('link', 'mytheme-icon-link');
  <!-- etc -->

The first ID, bars, is the default icon ID in Discourse and the second is the ID of your replacement icon. The easiest way to find an ID of one of our icons is to inspect the icon in your browser.

Here the icon name follows the d-icon- prefix. So in this example it’s d-unliked

Most of our icons follow the icon names from, but there are exceptions (which is why checking the ID in your inspector is the most reliable method). You can see all the exceptions in the const REPLACEMENTS block here on github.

That’s it. You can now style Discourse with your own custom icons!