[TechSpeak] How to Render SVG Images in Sitecore JSS

In one of our Sitecore-based projects, we faced the issue of rendering SVG images.

The thing is that Sitecore JSS doesn’t support the rendering of SVG images as <svg> </svg> elements.

Working with Sitecore 8, we had to extend the config file so that Media Library could support the SVG format.

With Sitecore 9, we get the support of an SVG format out of the box: you can add an SVG image to the Medial Library as an image template, and, consequently, a content author can use it anywhere as a regular image.

Sitecore 9.2 Sample:

SVG-image-rendering-in-Sitecore-image

SitecoreJSS-SVG-image-rendering-photo

As a result of such rendering, an SVG file will be embedded via an <img> element like a reference in the src attribute:

SitecoreJSS-SVG-rendering-image

Still, this way of rendering has some disadvantages (from the Mozilla site):

  • You cannot manipulate the image with JavaScript
  • If you want to control the SVG content with CSS, you must include inline CSS styles in your SVG code. (External stylesheets invoked from the SVG file take no effect.)
  • You cannot restyle the image with CSS pseudoclasses (like :focus).

So We Developed Another Solution

The way of rendering SVG images within the <img> element didn’t match the requirements of our project. We needed to have SVG images rendered as <svg> </svg> elements.

That’s why we decided to implement the following behavior:

  1. If the content author adds a non-SVG image (e.g. png, jpg, etc.) to the data source, the result will be a regular image
  2. If the content author applies SVG in the data source, the result will be like this:

SVG-image-rendering-Sitecore-9-image

Following are the instructions on how to implement #2 by extending some places in Sitecore:

1. Extend renderField pipeline by creating a new custom ImageRenderer processor inherited from Sitecore.Xml.Xsl.ImageRenderer (Sitecore.Kernel.dll) and override Render() method.


public override RenderFieldResult Render()
{
    if (Item == null || Parameters == null)
    {
        return RenderFieldResult.Empty;
    }    

    var field = Item.Fields[FieldName];
    if (field != null)
    {
        _imageField = new ImageField(field, FieldValue);        

        if (_imageField.MediaItem != null)
        {
            var imageMediaItem = new MediaItem(_imageField.MediaItem);            
            
            if (imageMediaItem.MimeType.Equals("image/svg+xml", StringComparison.Ordinal))
            {
                ParseNodeForSvg(Parameters);
                return new RenderFieldResult(RenderSvgImage(imageMediaItem));
            }
        }
    }    
    return base.Render();
}

This method checks if the MimeType type equals “image/svg+xml” and returns RenderFieldResult as a streamed media item string in the SVG XML format; otherwise it calls to the base rendering method.

2. Create a new GetImageFieldValue type inherited from Sitecore.Pipelines.RenderField.GetImageFieldValue (Sitecore.Kernel.dll) and override CreateRenderer method. This method creates a new instance of the CustomImageRenderer processor created in step #1.


public class CustomGetImageFieldValue : GetImageFieldValue
{
   protected override ImageRenderer CreateRenderer()
   {
     return new CustomImageRenderer();
   }
}

3. Extend getFieldSerializer pipeline by creating new custom  GetFieldSerializer (Sitecore.LayoutService.dll) processor and override SetResult method. This method sets the result of serialization into GetFieldSerializerPipelineArgs.Result:


public class CustomGetImageFieldSerializer : GetImageFieldSerializer
{
   public CustomGetImageFieldSerializer(IFieldRenderer fieldRenderer) : base(fieldRenderer)
   {
   }
 
   protected override void SetResult(GetFieldSerializerPipelineArgs args)
   {
       Assert.ArgumentNotNull(args, nameof(args));
       args.Result = new CustomImageFieldSerializer(FieldRenderer);
   }
}

4. Create a new custom ImageFieldSerializer type inherited from Sitecore.LayoutService.Serialization.FieldSerializers.ImageFieldSerializer (Sitecore.LayoutService.dll) and override ParseRenderedImage() method. This method parses the rendered field to Dictionary. If the rendered field has the SVG code, it will put this code into the dictionary with the “svgCode” key. Otherwise, this method will call to the base method:


class CustomImageFieldSerializer : LayoutService.Serialization.FieldSerializers.ImageFieldSerializer
{ 

    public CustomImageFieldSerializer(IFieldRenderer fieldRenderer) : base(fieldRenderer)
    {
    } 
    
    protected override IDictionary<string, string> ParseRenderedImage(string renderedField)
    {
        var dictionary = new Dictionary<string, string>(); 
        
        var startSvgTag = "<svg";
        var endSvgTag = "/svg>"; 

        var startIndex = renderedField.IndexOf(startSvgTag, StringComparison.Ordinal);
        if (startIndex != -1)
        {
            var lastIndex = renderedField.LastIndexOf(endSvgTag, StringComparison.Ordinal);
 
            var result = renderedField.Substring(startIndex, lastIndex - startIndex + endSvgTag.Length);
 
            dictionary.Add("svgCode", result);
            return dictionary;
        } 
        
        return base.ParseRenderedImage(renderedField);
  }
}

5. Create a configuration patch to apply the custom stuff implemented above:


<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/" xmlns:search="http://www.sitecore.net/xmlconfig/search/">
  <sitecore>
    <pipelines>
      <renderField>
        <processor        
          patch:instead="processor[@type='Sitecore.Pipelines.RenderField.GetImageFieldValue, Sitecore.Kernel']"     
          type="Sitecore.Foundation.Extensions.JSS.SVG.Pipelines.RenderField.CustomGetImageFieldValue, Sitecore.Foundation.Extensions.JSS.SVG"
        />
      </renderField>
      <group>
        <pipelines>
          <getFieldSerializer>
            <processor patch:before="*[@type='Sitecore.LayoutService.Serialization.Pipelines.GetFieldSerializer.GetImageFieldSerializer, Sitecore.LayoutService']"
                       type="Sitecore.Foundation.Extensions.JSS.SVG.Pipelines.ImageFieldSerializer.CustomGetImageFieldSerializer, Sitecore.Foundation.Extensions.JSS.SVG"
                       resolve="true" >
              <FieldTypes hint="list">
                <fieldType id="1">image</fieldType>
              </FieldTypes>
            </processor>
          </getFieldSerializer>
        </pipelines>
      </group>
    </pipelines>
  </sitecore>
</configuration>

6. In JSS, you can use the following logic:

SVG-images-rendering-Sitecore-photo

That is all! Happy coding Sitecorians! 😊

P.S. Many thanks to a colleague of mine who helped with investigating this topic.

All the source code you need you can find in GitHub.

Rating: 5.0/5. From 5 votes. Show votes.
Please wait...

About the author

Vadzim Papko
Vadzim Papko

Vadzim Papko is a Sitecore MVP, a senior Sitecore and .Net developer at SaM Solutions. Adhering to the principles of non-stop self-development, he devotes himself to Sitecore innovation and popularization. A certified Sitecore 9 and Xamarin developer. Find him on twitter: @knopikmag.