# Introduction
In this article I describe one of the feature we have developed for this site: the need to integrate diagrams, drawings and sketches.
But I had some requirements to satisfy:
- theme (color and fonts) support
- responsive
- rendered in React server components
This is an example of the result. Change the theme of the site to see it in action:
# Use Photo Editors?
The first idea was to create the images with our favorite editing software and use them in our site.
Some of my friends use Photoshop and Illustrator, I use the Affinity suite , others use Gimp . Everyone uses the software they are most comfortable with.
But how to support different themes, in our case dark and light?
Too much work 😅
But even if I created two different images, the solution did not excite me.
In fact, when user changes the theme we should switch the image from the "dark" version to the "light" one, and vice versa.
OK, let's imagine we want to implement this solution. The solutions I had in mind were essentially two:
We use Tailwind and one way to change an image based on theme could be to hide and show them based on current theme:
The main problem is that now I should load two different images and hide one of the them. Not a great solution for performance and traffic generated. There are other tricks for Tailwind that allow us to switch images but nothing that excited me
Another easy way to handle this issue is by using a client side approach: we may save the current theme in a global state and make our
<Img>
component reacts to changes, switching the src
image property at runtime and loading the right image when theme changes from dark to light.However, this solution was also not good, since I would have had to convert our custom server component
<Img>
that renders images into a client component, losing performance and rendering the images client side, with the risk of delay and flickering# SVG are vectorial!
The solution that I therefore considered most suitable was to create SVG images in which, being vectorial, I could apply different CSS styles on the
svg
, path
, text
and all the other elements that compose an SVG.The main convenience would have been to create a single image, do only one export and then apply with Tailwind the styles necessary to manage the different themes.
When we create an SVG image with our favourite tool, we need to define colors for lines, shapes, text and so on.
So, after exporting an SVG I would have had to manually replace all the inline styles with the Tailwind classes in order to support both themes, light and dark.
For example, I should update all
text
svg elements removing the inline style (fill
) and replace them with class
And I should do the same for all
path
elements and other SVG elements, manually updating stroke
, fill
and so on. Too much work. Again 😅
# Goal
I want to create a React component that loads a local SVG image from the NextJs
app
router folder (v.14+) and automatically applies colors and fonts based on the theme.BONUS: make the SVG responsive
Furthermore I wanted it to be done server side, before rendering.
So my wish was to:
- create a
<SVGLoader>
component that accept an SVG path:path="img.svg"
- this component must analyze the content of this
svg
, process it and replace all the inline styles and attributes generated by the tool with our own custom classes - avoid client side stuff: do everything before rendering
But there is still a problem:
I use Affinity Designer , Giorgio, co-author, uses Linux (and I don't know what software he's using 😅), another author might want to use Illustrator or something else.
It would be really complicated to manage all the cases and so I thought that a good strategy would be to use all the same (web-based) tool, which generates a very similar output.
So I thought to use Excalidraw , an excellent and popular tool for drawing diagrams and sketching, using a style very similar to our site.
So, the process to create and manipulate the SVG would be the folling:
We draw the image with Excalidraw
Excalidraw exports to SVGs but they are no responsive and there is no themes support.
As you can see in the code snippet below, the
svg
element has fixed values for its width
and height
attributes and the path
element uses an inline stroke
color:diagram.svg
We save the exported SVG file in our article folder (currently our articles are written using markdown, or rather MDX):
The MDX article simply use the svg with a dedicated component:
<Excalidraw path="./images/diagram1.svg" />
The SVG will be modified and rendered on server and shown with colors suitable for the dark and light theme:
See how it works by clicking on the settings icon at the bottom left and changing the current theme.
# The Idea
How to put my idea into practice?
We can simplify the process as follows:
Excalidraw.tsx
After several tests and experiments the final component should looks like the following:
Excalidraw.tsx
Usage:
article.mdx
# Add WebPack SVG loader
The first "problem" is that I couldn't load SVG in NextJS application by default, so I used the
@svgr/webpack
package and I modified the webpack configuration in next.config.mjs
adding a special loader for SVG:next.config.mjs
We also modified some presets to prevent the svg from being modified by the loader
# SVG: analyze and update
And now? What strategy to use to parse the SVG and replace the nodes?
First I had to analyze the SVG exported by Excalidraw:
- Line 3 needs to be modified to support responsive layout:
width="100%" height="100%"
- Line 7: we have to replace
fontFace
to use our font (IndieFlower) - In Line 16 and other svg elements we have to remove the
stroke
attributes in favor of CSS classes
diagram.svg
After a short search on Google and ChatGPT (😅) I found the library
xml-js
that allows me to easily parse the HTML structure of the SVG and easily make replaces.xml-js package convert XML text to Javascript object / JSON text (and vice versa).
And here is the script that loads, parses and updates the SVG before the render:
it's just a simple example to make you understand the concept
modifySvg.ts
So the SVG is modified before the render and now it automatically supports our theme. And it's responsive!
Excalidraw.tsx
Usage:
article.mdx
That's all
You can see the result directly in this article. Most of diagrams use our
<Excalidraw>
component.
Change the theme using the icon on bottom left side : )