Tag Archives: SVG

Create SVG React components using Adobe Experience Design

Creating beautiful and responsive web designs is a challenging task. HTML5 and CSS3 are out there for a while and are powerful tools for creating responsive web designs, but there’s another great technology for creating responsive designs as well: SVG! ūüôā

I’m not going to show you all the things you can do with SVG, if you like a very good introduction I can recommend Sarah Drasners Talk SVG can do that?!.

Instead I’m going to show you how you can use Adobe Experience Design as a starting point to create svg graphics, which you than can easily mold into a reusable and configurable react component. This could basically work with any other design tool, such as Sketch, Figma etc, you named it – as long they allow you to export graphics as svg.

So, let’s start. First off, we need an Adobe XD Prototype containing elements we could turn into react components. For this blog post, I’m going to use something from www.uplabs.com.

For example Nandu G created a good looking Dashboard:

Pro Stats Dashboard

Pro Stats Dashboard from Nandu G

Especially the Profile View Diagram looks like a good candiate for a svg react component. So, lets start:

To export the elements as svg we need to select the group containing the elements, right click and then “Export…”.¬† As Format we must choose “svg”, The other options are not relevant for this example. After clicking export we can open the svg.

The svg is straight forward, now custom namespaces or anything like that. We can take this and put it into a React Stateless Functional Component:

import React from 'react';

export default (props) => (
  <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="12137 3868 440 430">
  <defs>
    <style>
      .cls-1, .cls-10, .cls-2, .cls-8, .cls-9 {
        fill: #fff;
      }

      .cls-1 {
        opacity: 0.05;
      }

      .cls-2 {
        font - size: 18px;
        font-family: SegoeUI-Bold, Segoe UI;
        font-weight: 700;
        letter-spacing: 0.1em;
        opacity: 0.6;
      }

      .cls-3 {
        opacity: 0.298;
      }

      .cls-3, .cls-4 {
        fill: url(#linear-gradient);
      }

      .cls-4 {
        opacity: 0.2;
      }

      .cls-10, .cls-5, .cls-9 {
        opacity: 0.4;
      }

      .cls-5 {
        fill: url(#linear-gradient-3);
      }

      .cls-6 {
        opacity: 0.5;
        fill: url(#linear-gradient-4);
      }

      .cls-7 {
        fill: #111529;
      }

      .cls-8 {
        font - size: 31px;
      }

      .cls-10, .cls-8, .cls-9 {
        font - family: SegoeUI, Segoe UI;
      }

      .cls-9 {
        font - size: 15px;
      }

      .cls-10 {
        font - size: 14px;
      }

      .cls-11 {
        fill: #16cec0;
      }

      .cls-12 {
        fill: #2d8dfd;
      }
    </style>
    <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
      <stop offset="0" stop-color="#16cec0" />
      <stop offset="1" stop-color="#2d8dfd" />
    </linearGradient>
    <linearGradient id="linear-gradient-3" x1="0.798" y1="-0.177" x2="0.283" y2="1.342" xlink:href="#linear-gradient"/>
    <linearGradient id="linear-gradient-4" x1="0.692" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
      <stop offset="0" stop-color="#16cec0" />
      <stop offset="1" stop-color="#0062d5" />
    </linearGradient>
  </defs>
  <g id="Group_14" data-name="Group 14" transform="translate(12137 3868)">
    <rect id="Rectangle_5" data-name="Rectangle 5" class="cls-1" width="440" height="430" />
    <text id="PROFILE_VIEWS" data-name="PROFILE VIEWS" class="cls-2" transform="translate(40 54)"><tspan x="0" y="0">PROFILE VIEWS</tspan></text>
    <g id="Group_6" data-name="Group 6" transform="translate(89 79.244)">
      <g id="Group_5" data-name="Group 5" transform="translate(0 0)">
        <path id="Path_8" data-name="Path 8" class="cls-3" d="M49.97,48.808c18.3-25.5,6.732-39.034,26.444-46.1s27.18,8.618,51.577,8.618S162.132-6.667,179.373,2.7s1.863,26.69,17.58,46.1,38.113,7.3,45.286,31.549-16.6,36.406-16.6,65.435,28.164,28.764,16.6,48.155-34.3,12.621-62.866,29.409S153.73,258.511,127.99,261.1,99.849,242.346,76.413,233.7s-41.132,4.065-57.185-17.913-.077-37.105-7.028-70S-5.411,106.6,3.234,80.357,31.675,74.307,49.97,48.808Z" transform="translate(261.314 5.903) rotate(90)" />
        <path id="Path_5" data-name="Path 5" class="cls-4" d="M49.97,48.808c18.3-25.5,6.732-39.034,26.444-46.1s27.18,8.618,51.576,8.618S162.132-6.667,179.373,2.7s1.864,26.69,17.581,46.1,38.113,7.3,45.286,31.549-16.6,36.406-16.6,65.435,28.164,28.764,16.6,48.155-34.3,12.621-62.867,29.409S153.73,258.511,127.99,261.1,99.849,242.346,76.413,233.7s-41.132,4.065-57.185-17.913-.077-37.105-7.028-70S-5.411,106.6,3.233,80.357,31.675,74.307,49.97,48.808Z" transform="translate(261.314 5.903) rotate(90)" />
        <path id="Path_4" data-name="Path 4" class="cls-5" d="M44.041,39.159C59.835,18.327,49.853,7.27,66.869,1.5s23.464,7.041,44.525,7.041,40.354-9.752,55.237-2.1-9.271,16.86,4.3,32.719,32.9,5.966,39.095,25.774S195.7,94.674,195.7,118.39s16.75,35.343,6.764,51.185-22.052-1.533-46.709,12.181-19.525,18.97-41.745,21.084-26.906-5.57-47.137-12.63-49.955,18.118-63.174,0,13.742-44.947,7.741-71.819S-4.767,70.9,2.7,49.458,28.247,59.99,44.041,39.159Z" transform="matrix(0.966, -0.259, 0.259, 0.966, 0, 54.659)" />
        <path id="Path_3" data-name="Path 3" class="cls-6" d="M2555.706,835.7c15.777-19.9,5.806-30.47,22.8-35.988s23.44,6.728,44.479,6.728,29.444-14.043,44.312-6.728,1.608,20.835,15.162,35.988,32.869,5.7,39.054,24.627S2707.2,888.741,2707.2,911.4s24.289,22.453,14.313,37.59-29.584,9.852-54.216,22.957-22.114,27.442-44.312,29.461-24.269-14.638-44.479-21.384-49.9,17.312-63.109,0,13.728-42.948,7.733-68.625-15.188-30.6-7.733-51.079S2539.928,855.6,2555.706,835.7Z" transform="translate(-2489.581 -763.404)" />
        <line x1="0.5" y1="1" x2="0.5" y2="1" stroke="black" />
        <circle id="Ellipse_1" data-name="Ellipse 1" class="cls-7" cx="79.379" cy="79.379" r="79.379" transform="translate(50.679 54.563)" />
      </g>
      <text id="_9801" data-name="9801" class="cls-8" transform="translate(131 128.786)"><tspan x="-33.422" y="0">9801</tspan></text>
      <text id="New_Users" data-name="New Users" class="cls-9" transform="translate(130.999 158.209)"><tspan x="-35.054" y="0">New Users</tspan></text>
    </g>
    <g id="Group_7" data-name="Group 7" transform="translate(89 371)">
      <text id="_60_Followers" data-name="60% Followers" class="cls-10" transform="translate(19 15)"><tspan x="0" y="0">60% Followers</tspan></text>
      <circle id="Ellipse_2" data-name="Ellipse 2" class="cls-11" cx="4.5" cy="4.5" r="4.5" transform="translate(0 5)" />
    </g>
    <g id="Group_8" data-name="Group 8" transform="translate(231 371)">
      <text id="_40_New_Users" data-name="40% New Users" class="cls-10" transform="translate(19 15)"><tspan x="0" y="0">40% New Users</tspan></text>
      <circle id="Ellipse_2-2" data-name="Ellipse 2" class="cls-12" cx="4.5" cy="4.5" r="4.5" transform="translate(0 5)" />
    </g>
  </g>
</svg>)

But this will throw some errors, for example, JSX doesn’t support xml namespaces (like xlink:href) – Namespace tags are not supported, ReactJSX is not XML. Gladly, react allow to use camelCase notation, which will render the correct xml namespace. So all we have to do is to replace xmlns:xlink with xmlnsXlink and xlink:href with xmlHref.

The next problem comes from the style tag. The style definition contains curly braces, which causes some problems because JSX uses them to insert values from the component. The best way to solve this is to simply put the css style definitions where they belong, in a css file. One other workaround is described here: https://github.com/facebook/react/issues/2250#issuecomment-229468253

So, after this little modification our react components look like this:

import React from 'react';

export default (props) => (
  <svg xmlns="http://www.w3.org/2000/svg" xmlnXlink="http://www.w3.org/1999/xlink" viewBox="12137 3868 440 430">
  <defs>
    <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
      <stop offset="0" stop-color="#16cec0" />
      <stop offset="1" stop-color="#2d8dfd" />
    </linearGradient>
    <linearGradient id="linear-gradient-3" x1="0.798" y1="-0.177" x2="0.283" y2="1.342" xlinkHref="#linear-gradient"/>
    <linearGradient id="linear-gradient-4" x1="0.692" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
      <stop offset="0" stop-color="#16cec0" />
      <stop offset="1" stop-color="#0062d5" />
    </linearGradient>
  </defs>
  <g id="Group_14" data-name="Group 14" transform="translate(12137 3868)">
    <rect id="Rectangle_5" data-name="Rectangle 5" class="cls-1" width="440" height="430" />
    <text id="PROFILE_VIEWS" data-name="PROFILE VIEWS" class="cls-2" transform="translate(40 54)"><tspan x="0" y="0">PROFILE VIEWS</tspan></text>
    <g id="Group_6" data-name="Group 6" transform="translate(89 79.244)">
      <g id="Group_5" data-name="Group 5" transform="translate(0 0)">
        <path id="Path_8" data-name="Path 8" class="cls-3" d="M49.97,48.808c18.3-25.5,6.732-39.034,26.444-46.1s27.18,8.618,51.577,8.618S162.132-6.667,179.373,2.7s1.863,26.69,17.58,46.1,38.113,7.3,45.286,31.549-16.6,36.406-16.6,65.435,28.164,28.764,16.6,48.155-34.3,12.621-62.866,29.409S153.73,258.511,127.99,261.1,99.849,242.346,76.413,233.7s-41.132,4.065-57.185-17.913-.077-37.105-7.028-70S-5.411,106.6,3.234,80.357,31.675,74.307,49.97,48.808Z" transform="translate(261.314 5.903) rotate(90)" />
        <path id="Path_5" data-name="Path 5" class="cls-4" d="M49.97,48.808c18.3-25.5,6.732-39.034,26.444-46.1s27.18,8.618,51.576,8.618S162.132-6.667,179.373,2.7s1.864,26.69,17.581,46.1,38.113,7.3,45.286,31.549-16.6,36.406-16.6,65.435,28.164,28.764,16.6,48.155-34.3,12.621-62.867,29.409S153.73,258.511,127.99,261.1,99.849,242.346,76.413,233.7s-41.132,4.065-57.185-17.913-.077-37.105-7.028-70S-5.411,106.6,3.233,80.357,31.675,74.307,49.97,48.808Z" transform="translate(261.314 5.903) rotate(90)" />
        <path id="Path_4" data-name="Path 4" class="cls-5" d="M44.041,39.159C59.835,18.327,49.853,7.27,66.869,1.5s23.464,7.041,44.525,7.041,40.354-9.752,55.237-2.1-9.271,16.86,4.3,32.719,32.9,5.966,39.095,25.774S195.7,94.674,195.7,118.39s16.75,35.343,6.764,51.185-22.052-1.533-46.709,12.181-19.525,18.97-41.745,21.084-26.906-5.57-47.137-12.63-49.955,18.118-63.174,0,13.742-44.947,7.741-71.819S-4.767,70.9,2.7,49.458,28.247,59.99,44.041,39.159Z" transform="matrix(0.966, -0.259, 0.259, 0.966, 0, 54.659)" />
        <path id="Path_3" data-name="Path 3" class="cls-6" d="M2555.706,835.7c15.777-19.9,5.806-30.47,22.8-35.988s23.44,6.728,44.479,6.728,29.444-14.043,44.312-6.728,1.608,20.835,15.162,35.988,32.869,5.7,39.054,24.627S2707.2,888.741,2707.2,911.4s24.289,22.453,14.313,37.59-29.584,9.852-54.216,22.957-22.114,27.442-44.312,29.461-24.269-14.638-44.479-21.384-49.9,17.312-63.109,0,13.728-42.948,7.733-68.625-15.188-30.6-7.733-51.079S2539.928,855.6,2555.706,835.7Z" transform="translate(-2489.581 -763.404)" />
        <line x1="0.5" y1="1" x2="0.5" y2="1" stroke="black" />
        <circle id="Ellipse_1" data-name="Ellipse 1" class="cls-7" cx="79.379" cy="79.379" r="79.379" transform="translate(50.679 54.563)" />
      </g>
      <text id="_9801" data-name="9801" class="cls-8" transform="translate(131 128.786)"><tspan x="-33.422" y="0">9801</tspan></text>
      <text id="New_Users" data-name="New Users" class="cls-9" transform="translate(130.999 158.209)"><tspan x="-35.054" y="0">New Users</tspan></text>
    </g>
    <g id="Group_7" data-name="Group 7" transform="translate(89 371)">
      <text id="_60_Followers" data-name="60% Followers" class="cls-10" transform="translate(19 15)"><tspan x="0" y="0">60% Followers</tspan></text>
      <circle id="Ellipse_2" data-name="Ellipse 2" class="cls-11" cx="4.5" cy="4.5" r="4.5" transform="translate(0 5)" />
    </g>
    <g id="Group_8" data-name="Group 8" transform="translate(231 371)">
      <text id="_40_New_Users" data-name="40% New Users" class="cls-10" transform="translate(19 15)"><tspan x="0" y="0">40% New Users</tspan></text>
      <circle id="Ellipse_2-2" data-name="Ellipse 2" class="cls-12" cx="4.5" cy="4.5" r="4.5" transform="translate(0 5)" />
    </g>
  </g>
</svg>)

This will render the demo component. All we have to do now is replace static text with real values. For example, the linear gradient offset should be adjusted to show how many profile views are from new users and how many are from people who follow us already. Also, the percentage at the bottom of the component should be updated accordingly to our users. The same goes for the “New User” counter in the center circle.

So let’s add some math and curly braces and voil√†:

import React from 'react';

export default (props) => {
  const onePercent = (props.newUsers + props.followers) / 100;
  const newUserPercent = Math.round(props.newUsers / onePercent);
  const followerPercent = Math.round(props.followers / onePercent);
  return (<svg xmlns="http://www.w3.org/2000/svg" xmlnsXlink="http://www.w3.org/1999/xlink" viewBox="12137 3868 440 430">
    <defs>
      <linearGradient id="linear-gradient" x1="0.5" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
        <stop offset={followerPercent / 100} stop-color="#16cec0" />
        <stop offset="1" stop-color="#2d8dfd" />
      </linearGradient>
      <linearGradient id="linear-gradient-3" x1="0.798" y1="-0.177" x2="0.283" y2="1.342" xlinkHref="#linear-gradient"/>
    <linearGradient id="linear-gradient-4" x1="0.692" x2="0.5" y2="1" gradientUnits="objectBoundingBox">
        <stop offset={followerPercent / 100} stop-color="#16cec0" />
        <stop offset="1" stop-color="#0062d5" />
      </linearGradient>
    </defs>
    <g id="Group_14" data-name="Group 14" transform="translate(12137 3868)">
      <rect id="Rectangle_5" data-name="Rectangle 5" class="cls-1" width="440" height="430" />
      <text id="PROFILE_VIEWS" data-name="PROFILE VIEWS" class="cls-2" transform="translate(40 54)"><tspan x="0" y="0">PROFILE VIEWS</tspan></text>
      <g id="Group_6" data-name="Group 6" transform="translate(89 79.244)">
        <g id="Group_5" data-name="Group 5" transform="translate(0 0)">
          <path id="Path_8" data-name="Path 8" class="cls-3" d="M49.97,48.808c18.3-25.5,6.732-39.034,26.444-46.1s27.18,8.618,51.577,8.618S162.132-6.667,179.373,2.7s1.863,26.69,17.58,46.1,38.113,7.3,45.286,31.549-16.6,36.406-16.6,65.435,28.164,28.764,16.6,48.155-34.3,12.621-62.866,29.409S153.73,258.511,127.99,261.1,99.849,242.346,76.413,233.7s-41.132,4.065-57.185-17.913-.077-37.105-7.028-70S-5.411,106.6,3.234,80.357,31.675,74.307,49.97,48.808Z" transform="translate(261.314 5.903) rotate(90)" />
          <path id="Path_5" data-name="Path 5" class="cls-4" d="M49.97,48.808c18.3-25.5,6.732-39.034,26.444-46.1s27.18,8.618,51.576,8.618S162.132-6.667,179.373,2.7s1.864,26.69,17.581,46.1,38.113,7.3,45.286,31.549-16.6,36.406-16.6,65.435,28.164,28.764,16.6,48.155-34.3,12.621-62.867,29.409S153.73,258.511,127.99,261.1,99.849,242.346,76.413,233.7s-41.132,4.065-57.185-17.913-.077-37.105-7.028-70S-5.411,106.6,3.233,80.357,31.675,74.307,49.97,48.808Z" transform="translate(261.314 5.903) rotate(90)" />
          <path id="Path_4" data-name="Path 4" class="cls-5" d="M44.041,39.159C59.835,18.327,49.853,7.27,66.869,1.5s23.464,7.041,44.525,7.041,40.354-9.752,55.237-2.1-9.271,16.86,4.3,32.719,32.9,5.966,39.095,25.774S195.7,94.674,195.7,118.39s16.75,35.343,6.764,51.185-22.052-1.533-46.709,12.181-19.525,18.97-41.745,21.084-26.906-5.57-47.137-12.63-49.955,18.118-63.174,0,13.742-44.947,7.741-71.819S-4.767,70.9,2.7,49.458,28.247,59.99,44.041,39.159Z" transform="matrix(0.966, -0.259, 0.259, 0.966, 0, 54.659)" />
          <path id="Path_3" data-name="Path 3" class="cls-6" d="M2555.706,835.7c15.777-19.9,5.806-30.47,22.8-35.988s23.44,6.728,44.479,6.728,29.444-14.043,44.312-6.728,1.608,20.835,15.162,35.988,32.869,5.7,39.054,24.627S2707.2,888.741,2707.2,911.4s24.289,22.453,14.313,37.59-29.584,9.852-54.216,22.957-22.114,27.442-44.312,29.461-24.269-14.638-44.479-21.384-49.9,17.312-63.109,0,13.728-42.948,7.733-68.625-15.188-30.6-7.733-51.079S2539.928,855.6,2555.706,835.7Z" transform="translate(-2489.581 -763.404)" />
          <circle id="Ellipse_1" data-name="Ellipse 1" class="cls-7" cx="79.379" cy="79.379" r="79.379" transform="translate(50.679 54.563)" />
        </g>
        <text id="_9801" data-name="9801" class="cls-8" transform="translate(131 128.786)"><tspan x="-33.422" y="0">{props.newUsers}</tspan></text>
        <text id="New_Users" data-name="New Users" class="cls-9" transform="translate(130.999 158.209)"><tspan x="-35.054" y="0">New Users</tspan></text>
      </g>
      <g id="Group_7" data-name="Group 7" transform="translate(89 371)">
        <text id="_60_Followers" data-name="60% Followers" class="cls-10" transform="translate(19 15)"><tspan x="0" y="0">{followerPercent}% Followers</tspan></text>
        <circle id="Ellipse_2" data-name="Ellipse 2" class="cls-11" cx="4.5" cy="4.5" r="4.5" transform="translate(0 5)" />
      </g>
      <g id="Group_8" data-name="Group 8" transform="translate(231 371)">
        <text id="_40_New_Users" data-name="40% New Users" class="cls-10" transform="translate(19 15)"><tspan x="0" y="0">{newUserPercent}% New Users</tspan></text>
        <circle id="Ellipse_2-2" data-name="Ellipse 2" class="cls-12" cx="4.5" cy="4.5" r="4.5" transform="translate(0 5)" />
      </g>
    </g>
  </svg>
)}

Usage:

<ProfileViews followers={4000} newUsers={4000} />

A next step could be to add some nix hover effects or animations.

As mentioned at the beginning, this exported assets are only a starting point for creating components. To make this workflow a little bit smoother here some tips:

  1. Before exporting a group from Adobe XD, create a new canvas with the height and width of this group and align the canvas to the 0,0 coordinates.
    Alignment of a canvas

    Alignment of a canvas in Adobe XD

    This yields much better translation coordinates in the exported svg. As in the example above the viewbox of the exported svg is set to¬†viewBox=”12137 3868 440 430″, Also the Translation of “translate(12137 3868)” the first group is somewhat weird. Instead they should be¬†viewBox=”0 0 440 430″ and¬†translate(40 54). This trick is very helpful if you need to calculate translation in your component.

  2. The Naming of the group in the svg correlates directly to the naming in Adobe XD.  Creating the react component is a lot easier if you structure your prototype in Adobe XD nicely and give every element a descriptive name РI know, naming is a hard thing.

I created a codesandbox for this blog post sample, if you like, you can play a little bit with it.