use-svg-maker or creating SVGs with React, A Tutorial

SVGs are XML descriptions of vector graphics. React is a highly efficient, supported way to create XML-ish stuff. Indeed while dynamic, it is often used for purely static content too. So, what if instead of clicking your mouse a lot in Figma, you used React code to create SVGs? That is what use-svg-maker is for. It takes care of the tedious parts (which actually aren't that many) to allow you to rapidly create and save SVGs. We will create something like

Let's create a basic setup in Code Sandbox:

We've added the library, Tailwind for CSS and a helper for parameterising the SVGs. Now we'd better draw something. If you open the sandbox above you can code along easily with this tutorial. Open App.tsx and look for

<svg className=" bg-white" {...props} ref={ref}></svg>

This is just SVG. We also have access to some meta data from the hook. And we've very easily created a button to save the SVG. But we'd better draw somthing. Let's start with a circle in the middle.

<svg className=" bg-white" {...props} ref={ref}>
  <circle cx={meta.center.x} cy={meta.center.y} r={100} />
</svg>

This is a circle in SVG. It has all sorts of concise props for declaring shapes. By default the fill is black.

Okay that is boring and harder than Figma. Let's draw 50 circles, with fancy opacity variation. That would not be easy in Figma. And let's add a background too via a rect.

<svg className=" bg-white" {...props} ref={ref}>
  <rect
    x={0}
    y={0}
    width={meta.right}
    height={meta.bottom}
    fill={hsla(210, 90, 20)}
  />

  {[...Array(50).keys()].map((i) => (
    <circle
      key={i}
      r={meta.bottom / 10}
      cx={meta.center.x + meta.right * 0.25 * Math.cos((Math.PI * i) / 25)}
      cy={meta.center.y + meta.right * 0.25 * Math.sin((Math.PI * i) / 25)}
      fill={hsla(130 + i, 90, 70, 0.4)}
    />
  ))}
</svg>

Let's make it interactive. We will use leva, it has a wonderfully quick and concise (and typesafe) API for letting the user pick values: useControls. In just a few lines of code we can set up a way to explore parameters:

const { mainHue, background } = useControls({
  mainHue: {
    value: 130,
    min: 0,
    max: 360,
  },
  background: "#3366aa",
})

Let's go a bit further. Playing around with the leva controls we might decide that 330 is a good base hue and that #141f2e works for the background.

Let's do something more interesting based on that. use-svg-maker comes with helpers for noise and pseudo-randomness. Let's add some noise to the circles' positions. Add a new controlled parameter:

const { mainHue, background, randomness } = useControls({
  mainHue: {
    value: 330,
    min: 0,
    max: 360,
  },
  background: "#141f2e",
  randomness: {
    value: 0.3,
    min: 0,
    max: 1,
  },
})

And wire up like:

{
  ;[...Array(50).keys()].map((i) => (
    <circle
      key={i}
      r={meta.bottom / 10}
      cx={
        meta.center.x +
        meta.right * 0.25 * Math.cos((Math.PI * i) / 25) +
        meta.right * randomness * perlin2(i, 0.23424)
      }
      cy={meta.center.y + meta.right * 0.25 * Math.sin((Math.PI * i) / 25)}
      fill={hsla(mainHue + i, 90, 70, 0.4)}
    />
  ))
}

Abstraction

A lot of the code we looked at was pretty repetitive and verbose. But remember, we are using React. When we have figured out something we want to work with, it is as easy as it would be for a web app to make a component or hook with the logic. For example here is a small but non trivial component from our work:

function Circle({
  i,
  meta,
  randomness,
  mainHue,
}: {
  i: number
  randomness: number
  mainHue: number
  meta: ReturnType<typeof useSVG>["meta"]
}) {
  return (
    <circle
      key={i}
      r={meta.bottom / 10}
      cx={
        meta.center.x +
        meta.right * 0.25 * Math.cos((Math.PI * i) / 25) +
        meta.right * randomness * perlin2(i, 0.23424)
      }
      cy={meta.center.y + meta.right * 0.25 * Math.sin((Math.PI * i) / 25)}
      fill={hsla(mainHue + i, 90, 70, 0.4)}
    />
  )
}

With React components we can encapsulate and reuse code. With just a tiny amount more work we can create this configurable scene, with a reusable component. We pull in use-svg-maker's built in pseudo random number generator for another effect. This also includes a parameterised blur filter, one of SVG's more advanced effects.