e776299 Oct 10, 2016
@deepsweet @pra85 @JonathanStoye @GreLI
233 lines (181 sloc) 7.13 KB



So, as mentioned earlier, SVGO has a plugin-based architecture and almost every optimization is a separate plugin.

Plugins can remove and modify SVG elements, collapse contents, move attributes and do any other actions you want.

How it works

1. config

SVGO reads, parses and/or extends the default config, which contains all the SVGO settings, including plugins. Something like this:

  - myTestPlugi
  - myTestPlugin2: false
  - myTestPlugin3:
      param1: 1
      param2: 2

It's important to note that every plugin:

  • has its specific position in the plugins list,
  • can be turned on with name: true and off with name: false,
  • can have its own params which will be available later inside a plugin.

These settings can be changed by the provided config file with --config command line option. You can force using only your settings with the full: true parameter in your config:

full: true
  - myTestPlugin
  - myTestPlugin3:
      param1: 1
      param2: 2

In such a case only listed plugins will be run.

2. svg2js

SVGO converts SVG-as-XML data into SVG-as-JS AST representation. Something like this:

<?xml version="1.0" standalone="no"?>
<!-- Generator: Adobe Illustrator 16.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "">
<svg version="1.1" xmlns="">
    <script><![CDATA[ alert('hello'); ]]></script>
    content: [
            processinginstruction: { name: 'xml', body: 'version="1.0" standalone="no"' }
            doctype: ' svg PUBLIC "-//W3C//DTD SVG 1.1//EN" ""'
            comment: 'Generator: Adobe Illustrator 16.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)'
            elem: 'svg',
            prefix: '',
            local: 'svg',
            attrs: {
                version: {
                    name: 'version',
                    value: '1.1',
                    prefix: '',
                    local: 'version'
                xmlns: {
                    name: 'xmlns',
                    value: '',
                    prefix: 'xmlns',
                    local: ''
            content: [
                    elem: 'text',
                    prefix: '',
                    local: 'text',
                    content: [ { text: 'test' } ]
                    elem: 'script',
                    prefix: '',
                    local: 'script',
                    content: [ { cdata: ' alert(\'hello\'); ' } ]


It's important to note that:

  • there are special object keys to represent various SVG nodes: elem, processinginstruction, doctype, comment, cdata and text,
  • content is always an array,
  • attrs object keys represents a full attr name with namespace if it is, and all the details are inside as the prefix and local parts.

3. plugins

SVGO applies all plugins from the config to AST data. See a lot of examples in the plugins directory above.

3.1 types

In the simplest case plugins applying process can be represented as "each plugin runs over all AST data items and perform some actions". But 90% of typical optimizations requires some actions only on one (current) item from the data, so there is no sense to copypaste a recursive per-item loop every time on every plugin. And that's why we have a three types of plugins:

  • perItem - plugin works only with one current item, inside a "from the outside into the depths" recursive loop (default),
  • perItemReverse - plugin works only with one current item, inside a "from the depths to the outside" recursive loop (useful when you need to collapse elements one after other),
  • full - plugin works with the full AST and must returns the same.

perItem and perItemReverse plugins runs inside the recursive Array.prototype.filter loop, so if a plugin wants to remove current item then it should just return false.

But that's not all ;). We got rid of a loop copypasting, but every plugin still runs over all the SVG-as-JS data, which is not very optimal. Actually, at the first step where we got a config, there was a collapsing of consecutive plugins with the same type, so ultimately one loop wraps a group of plugins:


  - [perItem group],
  - [perItemReverse group],
  - [perItem group],
  - [full group]


3.2 API

And of course, writing plugins would not have been so cool without some sugar API:

  • Determine if item is an element (any, with a specific name or in a names array).
  • @param {String|Array} [param] element name or names arrays
  • @return {Boolean}
  • Determine if element is empty.
  • @return {Boolean}
  • Renames an element.
  • @param {String} name new element name
  • @return {Object} element
  • Perform a deep clone of this node.
  • @return {Object} element
hasAttr([attr], [val])
  • Determine if element has an attribute (any, or by name or by name + value).
  • @param {String} [name] attribute name
  • @param {String} [val] attribute value (will be toString()'ed)
  • @return {Boolean}
attr(name, [val])
  • Get a specific attribute from an element (by name or name + value).
  • @param {String} name attribute name
  • @param {String} [val] attribute value (will be toString()'ed)
  • @return {Object|Undefined}
removeAttr(name, [val])
  • Remove a specific attribute (by name or name + val).
  • @param {String} name attribute name
  • @param {String} [val] attribute value
  • @return {Boolean}
  • Add an attribute.
  • @param {Object} attr attribute object
  • @return {Object} created attribute
eachAttr(callback, [context])
  • Iterates over all attributes.
  • @param {Function} callback
  • @param {Object} [context] callback context
  • @return {Boolean} false if there are no any attributes

3.3 tests

There is nothing easier than testing your plugin:

  1. put myPlugin.01.svg in test/plugins like:
[original svg]


[how svg should look afterwards]


attributes if yout plugin needs them
  1. run npm test
  2. PROFIT!

You can see a lot of examples in the test/plugins directory.

4. js2svg

SVGO converts AST back into SVG-as-XML data string.

What's next

  1. Write your own plugin :) or
  2. Give me an idea of new optimization or API method