Modifying SVG files

Usage no npm install needed!

<script type="module">
  import codemenSvgModifier from 'https://cdn.skypack.dev/@codemen/svg-modifier';


SVG Modifier

Upload, update and save svg in a new & updated form. Make sure relevant simplebar version's css is imported / included.


import React, { Component } from 'react';
import SVGModifier from '@codemen/svg-test';

function Test (props) {
    return (



file?: string | object File

A url of the file or the file as a an object (Blob / File) - both accepted.

autoDimension?: boolean

Sets the width and height of the svg to 100% when its requested to be saved.

onNodeInsertion?: (node: object HTMLElement, layer: object Proxy(HTMLElement)) => undefined

Triggered whenever a node / zone is inserted, using pencil / pen / rectangle etc. tool.

  • node The html element that was just created

  • layer The html element that is containing the created the zone / element. It's a Proxy object, which provides all the attributes / props a regular html element object does and extends further.

onNodeRemoval?: (node: object HTMLElement, layer: object Proxy(HTMLElement)) => undefined

Triggered whenever a node / zone is deleted.
Parameters are the same as onNodeInsertion function.

onNodeMerge?: (data: object, layer: object Proxy(HTMLElement)) => undefined

Invoked as multiple zones of the same layer is merged.

  • data Has the following attributes

    • elements The elements that are being merged
    • mergedElement New merged element
  • layer HTML element as a Proxy object

onLayerInsertion?: (layer: object Proxy(HTMLElement), svgAbstractLayers: Array) => undefined

Invoked on new layer creation.

  • layer Newly created HTML element as a Proxy object

  • svgAbstractLayers Collection of existing (Proxy) layers

onBeforeLayerRemoval?: (layer: object Proxy(HTMLElement)) => boolean

Called as a layer is requested to be removed.

  • layer The HTML Proxy element that is requested to be deleted.

Return false (boolean) to prohibit layer removal.

onLayerRemoval?: (layer: object Proxy(HTMLElement), svgAbstractLayers: Array) => undefined

Invoked as the layer is successfully removed from the svg.

  • layer Removed HTML Proxy element

  • svgAbstractLayers Collection of existing (Proxy) layers

onLayerNameChange?: (layer: object Proxy(HTMLElement), value: string) => undefined

Prompted as layer name gets changed.

  • layer The HTML Proxy element

  • value New name

onLayerActivation?: (layer: object Proxy(HTMLElement)) => undefined

Called once a layer is set active.

  • layer The HTML Proxy element

onLayerViewToggle?: (layer: object Proxy(HTMLElement), visible: boolean) => undefined

Called as a layer is switched between hidden / visible state.

  • layer The HTML Proxy element

  • visible If the layer is switched to visible state.

onAttrModification?: (attr, node, action) => undefined

Called as the attributes of the zones / elements are modified.

  • attr Name of the attribute that has been affected. Still experimental, should not be relied upon completely.

  • node The HTML element being worked on

  • action Name of the action - insert (New attribute added) | remove (Attribute removed) | change (Attribute value modified)

onBeforeSVGMount?: (svgElem: object HTMLElement) => object

Just as the libary prepares the svg html and gets set to insert it into the DOM, this is invoked if any final tweaking is required.

  • svgElem The svg HTML Element that is expected to be inserted.

Return a list of layers like the following (please go through the whole example, more instructions expected inside) -

// `getSVGNode` defined later
var tempPath1 = getSVGNode('path'),
    tempRect1 = getSVGNode('rect'),
    tempPath2 = getSVGNode('path'),
    tempRect2 = getSVGNode('rect');

// Each element of the response array represents an array

// First layer will by default contain both the default floorplan along with the zones sent from the following array.

tempRect1.setAttribute('fill', red);
tempRect1.setAttribute('x', '20');
tempRect1.setAttribute('y', '20');
tempRect1.setAttribute('width', '100');
tempRect1.setAttribute('height', '100');

var sampleOnBeforeSVGMountResponse = [
        children: [
            // Following list are the zones of this layer
        props: {
            // Following attributes are reserved for specific purposes
            // 1. hidden -> set `true` | `false` to set initial visibility state, depending on this, layer elements `transform` attribute is set to `scale(0)` or `scale(1)`
            // 2. value -> To set initial layer name, this also sets layer elements `value` attribute
            // 3. is-mask-layer -> set `true` | `false` to determine if the layer works as a mask. Sets the `is-mask-layer` attribute of the layer to `` (empty value).
            // 4. internal-mask-id -> If provided, the `mask` html element that will contain the mask zones will possess this value as its `id`.

            // Rest of the values will be added to the layer elements attribute list
            value: 'Floorplan',
            test: 'Hello World' // This means that layer.getAttribute('test') === 'Hello World' 
        subLayers: []
    }, {
        children: [
        props: {
            value: 'Business'
        subLayers: [
            // A layer can have multiple sub-layers
                // Each sub-layer has to be configured the same way a layer is done,
                // except it wont have any `subLayers`
                children: [
                props: {
                    value: 'Sub-layer 1'

// An empty array could also be returned,
// returning an empty array is equal to sending the following
            children: [],
            props: {},
            subLayers: []
// meaning that there will only be the one layer which is mandatory

return sampleOnBeforeSVGMountResponse;

onSVGMount?: (svgAbstractLayers: Array, svgElem: object HTMLElement) => undefined

Invoked as the svg is mounted.

  • svgAbstractLayers Collection of existing (Proxy) layers

  • svgElem The svg HTML Element

onZoom?: (transform: object) => undefined

Called as the svg is zoomed in / out or panned.

  • transform d3 library received transform value, please refer to the following site if interested to know more on details.

D3 Zoom

onModeChange?: (activeMode: string) => undefined

Invoked as the active control switch is altered.

  • activeMode Probable values - select | curve | rectangle | polyline | zoom.
    These values can be subject to change in the future.

onInfoNodeMount?: (infoNode: object HTMLElement, node: object HTMLElement)

When the editor is switched to render created elements' / zones' information inside the active layer (usual view displays the svg) - whenever a node is mounted representing a zone, this callback is triggered.

  • infoNode The HTML Element that has just mounted

  • node The HTML Element zone that is being represented

onInfoNodeUnmount?: (infoNode: object HTMLElement, node: object HTMLElement)

When the editor is switched to svg view from created elements' / zones' information view - each node (representing a single zone) is unmounted and callback triggered consequently.

  • infoNode The HTML Element that has just unmounted

  • node The HTML Element zone that is being represented

getInfoComponent?: (infoProps: object) => React.Component

Instead of built-in zone information view, use this to render customized version as per requirement.

  • infoProps Object consisting of the following keys
    • key A unique value to let the component persist if needed - usually the value of the layer.
    • layer Active Proxy HTML Element
    • activeElement The HTML Element that is selected (active) at the moment
    • onNodeClick: (node: object HTMLElement) Default behavior on clicking a info-node. Pass the zone (not the info node, rather the html element that it represent) in the first parameter.
    • onNodeMount onInfoNodeMount props previously configured is passed through this attribute
    • onNodeUnMount onInfoNodeUnmount props previously configured is passed through this attribute

getZoneSettingsComponent?: (element: object HTMLElmeent) => React.Component

If zone needs to configure advanced settings of its own apart from regular setting up attributes.

  • element The HTML element requesting to render zone advanced settings UI

getLayerSettingsComponent?: (props: object) => React.Component

Same as getZoneSettingsComponent, but for individual layers.

  • props Object having the following attributes -
    • layer The layer the settings will be based on

getZoneName?: (zone: object HTMLElement) => string

Comes in handy when zone needs to presented by a name, for instance - when the zones are in between merging state.

  • zone The zone HTML element

onBeforeSave?: (file: object Blob) => boolean

When the Save button is clicked, instead of creating a new svg file and triggering download, onBeforeSave can receive the file and take control of saving.

  • file Newly created svg as a file

Return false (boolean) to skip default behavior (which is to create svg file and trigger download)

resolveUrl?: (url: string, callBack: (content: object Blob)) => boolean

If the url to fetch file from isn't the absolute one or if fetching the svg file needs custom handling, or if the svg file is already in store for the current url, resolveUrl callback can be utilized.

  • url Current url

  • callBack Send the content using this function, using the first parameter to pass the content (svg html).

Return false to return control back to svg-modifier library.

onResolvingUrl?: (url: string, content: string) => undefined

Invoked after a url is resolved.

  • url The url that has been resolved

  • content The svg html content that has been generated

onBeforeLayerReorder?: (layers: Array, oldIndex: number, newIndex: number) => boolean

Called as the layers / sub-layers are reordered by drag-n-dropping.

  • layers Current list of Proxy HTML element layers (might be sub-layers, if reorder occurs inside a layer's sub-layer list)

  • oldIndex The layer placed in oldIndex position is requested to be moved.

  • newIndex The layer placed in oldIndex position is requested to be moved to this newIndex position.

Return false (boolean) to reject reordering, else return true (boolean).

onLayerReorder?: (layers: Array) => undefined

Called as the layers / sub-layers successfully complete reordering.

  • layers Updated list of Proxy HTML element layers (might be sub-layers, if reorder occurs inside a layer's sub-layer list)

tabs?: Array

Extra set of tabs to render after the primary tabs on right sidebar. Expects an array, each element an object having the following attributes

  • id Unique identifier

  • name The name to appear on the tab

  • render?: () => React.Component A function that returns the React component to render the tab content.

permission?: object

Please refer to the following link -

logo?: string

Url of the logo to display on the top section of the svg-modifier.




import RelatedZones from '@codemen/svg-test/lib/components/RelatedZones';

A component to render UI in order to present all the related zones in terms of an HTML element.
Component expects the following props

  • zonal?: boolean Set true, else the component discards relation computation

  • element?: object HTMLElement The element the relation is computed in comparison to

  • layers?: Array List of layers to consider while extracting zones and computing relation

  • onZoneClick?: (node: object HTMLElement) On clicking an element inside the RelatedZones component, the zone referred by that element is passed as an argument in the first parameter.

  • zoneRenderer?: (props: object) => React.Component Used if each of the components representing a single zone is expected to be rendered in a customized way. If zoneRenderer is used, onZoneClick gets nullified.

First parameter props contain the following attributes, render a new component using this attributes, any attributes can be made use of or skipped.

  • node The HTML element zone the component represents

  • className Default className of the component's root element

  • onMouseEnter Default behavior on mouse-enter

  • onMouseLeave Default behavior on mouse-leave


setMaskLayer: (layer: object HTMLElement, customMaskID: string) => object HTMLElement

import { setMaskLayer } from '@codemen/svg-test';

Call passing a layer and an optional mask-id to configure a regular layer as a mask layer.

  • layer HTML element being used a layer

  • customMaskID A custom mask id to insert into the new HTML mask element - if required, else a random id will be configured.

Returned value is the HTML mask element.

releaseMaskLayer: (layer: object HTMLElement) => undefined

import { releaseMaskLayer } from '@codemen/svg-test';

A layer which has been configured using setMaskLayer, can be rolled back to its previous state using releaseMaskLayer.

  • layer HTML layer element

getBBox: (node: object HTMLElement) => object

import getBBox from '@codemen/svg-test/lib/utils/SVGUtils/getBBox';

Unlike the regular getBBox, this function takes transformation into account and returns with more related points.

  • node A zone as an HTML element

getBasicAttributes: (node: object HTMLElement) => object

import getBasicAttributes from '@codemen/svg-test/lib/utils/SVGUtils/General/getBasicAttributes';

Returns basic attributes (for instance, d is a basic attribute for path tag, data-test is not a basic attribute for any of the tags) along with their values.

  • node A zone as an HTML element

getTransformMatrix: (node: object HTMLElement) => object

import getTransformMatrix from '@codemen/svg-test/lib/utils/SVGUtils/General/getTransformMatrix';

Returns the matrix that is applied on the given node.

  • node A zone as an HTML element

getBaseMatrix: () => object

import { getBaseMatrix } from '@codemen/svg-test/lib/utils/SVGUtils/General/getTransformMatrix';

Returns a new copy of the base matrix.

getZoneRelations: (node: object HTMLElement) => object

import getZoneRelations from '@codemen/svg-test/lib/utils/SVGUtils/getZoneRelations';

Returns a new object containing all the other zones that are related to the given zones.

  • node Any zone as HTML element

getSVGNode: (tag: string) => object HTMLElement

import getSVGNode from '@codemen/svg-test/lib/utils/SVGUtils/General/getSVGNode';

Returns a brand new HTML Element using the given tag (not connected to DOM initially).

  • tag Any acceptable svg tags - g | rect | path etc.

getCircle: (x: number, y: number, stroke: string) => object HTMLElement

import getCircle from '@codemen/svg-test/lib/utils/SVGUtils/Test/getCircle';

Given the co-ordinates, returns a brand new circle situated at that point (yet to be inserted into the DOM).

  • x x co-ordinate

  • y y co-ordinate

  • stroke (optional) Color of the border

getRelation: (element1: object HTMLElement, element2: object HTMLElement, getIntersectionInfo: boolean) => object

import getRelation from '@codemen/svg-test/lib/utils/SVGUtils/getRelation';
import getRelation from '@codemen/svg-modifier/lib/utils/SVGUtils/getRelation';

Relation between two elements (whether they intersect each other or one inside another or not connected at all) are returned.

  • element1 Primary element

  • element2 Second element to compare with the primary element

  • getIntersectionInfo (optional) If true, only intersection related information is returned



import { CUSTOM_COMPONENT_CONTAINER_CLASS } from '@codemen/svg-test/lib/components/constants';

A classname appended to the html element (<g />) that contains all the custom created zones.
N.B. - The parentNode of this element (where the class is appended) is the layer HTML element.


Don't use it, previously intended to work with masking an individual zone, which is disabled at the moment.


Don't use it, previously intended to work with masking an individual zone, which is disabled at the moment.

Component API

import React, { Component } from 'react';
import SVGModifier from '@codemen/svg-test';

class Test extends Component {

    _onSVGMount() {
        var activeMode = this.refs['svg-modifier'].getActiveMode();

        alert('Current active mode is - `' + activeMode + '`');

    render() {
        return (

Component.getActiveMode?: () => string

Returns the current active mode - select | curve | rectangle | polyline | zoom.

Component.getHTMLLayer?: (element: object HTMLElement) => object HTMLElement

Pass any element and find out the containing layer.

  • element Any zones as an HTML element

Component.processNodeClick?: (node: object HTMLElement, multiple: boolean, force: boolean) => object HTMLElement

Programmatically click on a element / zone.

  • node An element / zone inside the layers

  • multiple Whether its part of a multiple selection operation - if so, passed node will be added to previous selections.

  • force Forcibly process selection even when its usually restricted - for instance, when multiple selection of nodes are in the middle of merging state.