Making SVG Accessible in Next.js with Typescript and MDX
Introduction
Before svgMagic
import React from 'react';
const ExampleComponent = () => (
<div>
<svg className="h-8 w-8 text-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 193.65 211.87">
<path fill="currentColor" d="M187.66,74.62l-5.07-5.76h0s.44-7.58.44-7.58c0-.32-.24-.7-.53-.85s-.74-.11-1,.09l-5.76,4.95h0s-7.66-.54-7.66-.54c-.32,0-.7.23-.84.52-.14.29-.1.73.1.99l5.07,5.76h0s-.44,7.58-.44,7.58c0,.32.24.7.53.85s.74.1,1-.09l5.75-4.94h0s.02,0,.02,0l7.65.54c.32,0,.7-.23.85-.52.14-.29.1-.73-.1-.99Z"/>
<path fill="currentColor" d="M156.77,41.25c-.07.73.37,1.64.99,2.04,0,0,0,0,0,0h0s0,0,0,0c.62.4,1.62.42,2.25.04l14.08-9.65.1-.06h.08s17.03,3.11,17.03,3.11c.73.08,1.63-.35,2.03-.95h.02s0-.01,0-.01c.39-.62.39-1.61.02-2.24l-9.96-14.16-.04-.06v-.12s2.86-16.83,2.86-16.83c.07-.73-.37-1.63-.99-2.02h0s0,0,0,0c0,0,0,0,0,0-.62-.4-1.64-.42-2.27-.05l-14.17,9.72-17.11-3.11c-.73-.08-1.64.35-2.03.96s-.4,1.62-.02,2.25l10.01,14.23-2.87,16.94Z"/>
<path fill="currentColor" d="M75.98,24.75l9.45,4.43,3.56,9.68c.18.4.68.75,1.12.77s.98-.27,1.19-.66l4.49-9.28,9.84-3.48c.4-.17.75-.67.77-1.1s-.27-.96-.66-1.17l-9.45-4.42-3.56-9.68c-.18-.4-.68-.74-1.12-.76s-.98.26-1.19.65l-4.49,9.29-9.84,3.48c-.4.18-.75.67-.77,1.1s.27.97.66,1.17Z"/>
<path fill="currentColor" d="M116.59,6.52l3.14,4.67-1.04,5.46c-.03.24.11.53.31.67s.53.15.73.03l4.65-3.05,5.52,1.12c.23.03.53-.1.66-.3s.14-.52.02-.73l-3.14-4.67,1.04-5.47c.03-.24-.11-.53-.31-.66s-.53-.15-.73-.03l-4.66,3.05-5.52-1.12c-.24-.03-.53.1-.66.3s-.14.52-.02.73Z"/>
<path fill="currentColor" d="M123.76,83.69l-14.09-10.66c-1.94-1.47-4.72-1.08-6.19.86l-10.66,14.09c-1.47,1.94-1.08,4.72.86,6.19l14.09,10.66c1.94,1.47,4.72,1.08,6.19-.86l10.66-14.09c1.47-1.94,1.08-4.72-.86-6.19Z"/>
<path fill="currentColor" d="M175.74,117.48c-6.56-4.96-15.42-4.59-21.58.29l-24.29-18.38,24.09-31.32c5.2-6.75,3.86-16.35-2.98-21.43l-12.38-9.2c-6.84-5.08-16.61-3.72-21.8,3.03l-23.84,30.99-24.33-18.4c3.02-7.25.96-15.89-5.6-20.85-7.78-5.89-18.86-4.35-24.75,3.43-5.89,7.78-4.35,18.86,3.43,24.75,6.56,4.96,15.42,4.59,21.58-.29l17.5,13.24c-15.2-1.63-30.72,2.34-43.28,11.56l-5.6-4.24c-5.83-4.42-14.15-3.26-18.56,2.57l-10.66,14.09c-4.41,5.84-3.26,14.15,2.57,18.56l14.09,10.66c5.84,4.41,14.15,3.26,18.56-2.57l10.66-14.09c4.42-5.84,3.26-14.15-2.57-18.56l-1.06-.8c11.62-7.64,25.78-10.37,39.3-7.74L10.86,178.16c-5.2,6.76-3.86,16.35,2.98,21.43l12.39,9.2c6.84,5.08,16.6,3.72,21.8-3.03l73.17-95.12c6.28,12.34,7.55,26.78,3.33,40.11l-1.06-.8c-5.84-4.41-14.15-3.26-18.56,2.57l-10.66,14.09c-4.41,5.84-3.26,14.15,2.57,18.56l14.09,10.66c5.83,4.41,14.15,3.26,18.56-2.57l10.66-14.09c4.42-5.84,3.26-14.15-2.57-18.56l-5.6-4.24c5.45-14.6,5.06-30.62-.65-44.8l17.5,13.24c-3.02,7.25-.96,15.89,5.6,20.85,7.78,5.89,18.86,4.35,24.75-3.43,5.89-7.78,4.35-18.86-3.43-24.75ZM59.41,51.63c-2.94,3.88-8.49,4.65-12.37,1.72-3.88-2.94-4.65-8.49-1.72-12.37,2.94-3.89,8.49-4.65,12.37-1.72,3.88,2.94,4.65,8.49,1.72,12.37ZM41.52,104.55l-10.66,14.09c-1.47,1.94-4.24,2.33-6.19.86l-14.09-10.66c-1.94-1.47-2.33-4.24-.86-6.19l10.66-14.09c1.47-1.94,4.24-2.33,6.19-.86l14.09,10.66c1.94,1.47,2.33,4.24.86,6.19ZM120.7,109.63c-4.48,5.52-12.54,6.57-18.26,2.25l-14.09-10.66c-5.72-4.32-6.9-12.37-2.8-18.18l8.23-10.95,2.66-3.52c4.41-5.84,12.72-6.99,18.56-2.57l14.09,10.66c5.84,4.41,6.99,12.73,2.57,18.56l-2.66,3.52-8.3,10.89ZM132.25,167.65c1.94,1.47,2.33,4.24.86,6.19l-10.66,14.09c-1.47,1.94-4.24,2.33-6.19.86l-14.09-10.66c-1.94-1.47-2.33-4.24-.86-6.19l10.66-14.09c1.47-1.94,4.24-2.33,6.19-.86l14.09,10.66ZM172.13,136.9c-2.94,3.88-8.49,4.65-12.38,1.72-3.88-2.94-4.65-8.49-1.71-12.37,2.94-3.89,8.49-4.65,12.37-1.72,3.88,2.94,4.65,8.49,1.72,12.37Z"/>
</svg>
</div>
);
export default ExampleComponent;
After svgMagic
import React from 'react';
import SVGM from "@/components/svgm/SVGM";
const ExampleComponent = () => (
<div>
<SVGM kind="svgm-mark" className="h-8 w-8 text-white" />
</div>
);
export default ExampleComponent;
Scalable Vector Graphics
. For an overview of the .svg
file format, see w3schools.com, adobe.com, or docusarus.io to familiarize yourself, if you'd like.
The .svg
format is used for vector based images. Since these are "scalable" they can be displayed at any resolution, on any supported device.
Because of this versatility, I lean on this format continually. Any time it can be called in, I go for it.
.svg
images are mathematically based images that can be scaled infinitely and maintain their quality. Pretty useful stuff in the realm of web design.
The Good, Bad and Ugly of SVG in Next.js
Below are some various examples of the standard ways we interact with .svg
files and SVG code currently, with example code blocks outlining the problems with these methods. This will effectively illustrate the problems that svgMagic solves.
SVG in a React Component
Typescript
// React Component Example (TypeScript)
import React from 'react';
const SvgExample: React.FC = () => (
<svg width="100" height="100" fill="currentColor">
<circle cx="50" cy="50" r="40" />
</svg>
);
export default SvgExample;
Cons:
- Have to create a new Typescript component for each
svg
, or you could have one Typescript component as anarray
orswitch
of them, but you still have to import them and update the list each time. - Extra step of copying your raw
.svg
file contents, or SVG code into a React component, or having to name an import for each.svg
file - Have to import the component(s) in each document, or
- Need to maintain a list of inline SVG code in a parent document, or
- Must specify an import every time a new SVG is added
Pros:
- Fine for ocassional usage and one-off situations if you don't work with SVG often.
- Theming works great once you switch fills to
currentColor
MDX
// React Component Example (MDX)
import { SvgExample } from './components/SvgExample';
<SvgExample />
Cons:
- Have to import each component (or a list of them all, which would still need to be updated each time) in
mdx-components
- Adds significant bulk and many lines of code to your
.mdx
- Difficult to read and parse
Pros:
- Fine for ocassional usage and one-off situations if you don't work with SVG often.
- Theming works great once you switch fills to
currentColor
SVG in an <img>
tag
Typescript
// SVG in an <img> tag (TypeScript)
import React from 'react';
import exampleBlackSvg from './example-light.svg';
import exampleWhiteSvg from './example-dark.svg';
const ImgExample: React.FC = () => (
<img src={exampleWhiteSvg} alt="Example SVG in White on Dark Background" className="hidden dark:flex"/>
<img src={exampleBlackSvg} alt="Example SVG in Black on Light Background" className="flex dark:hidden"/>
);
export default ImgExample;
Cons:
- Have to import each
.svg
you plan to use, this means two imports if you're using theming - Theming is "hacky", typically achieved by hiding a Light
<img>
element on a Light background, and showing it when the theme is changed to dark, and vice versa.
Pros:
- Fine for ocassional usage and one-off situations if SVG is infrequently used
- Simplifies usage by treating SVG's as static images
MDX
<img src="/path/to/example.svg" alt="Example SVG" />
Cons:
- Have to have multiple
<img>
tags if you want to be accessible and support dark mode, too. - Theming is "hacky", typically achieved by hiding a Light
<img>
element on a Light background, and showing it when the theme is changed to dark, and vice versa. - To support theming, multple versions have to be exported from your vector art program, and maintain two or more versions of almost the exact same source file.
Pros:
- No need for a Typescript parent component
- No need to define in
mdx-components
- No need to import each
.svg
directly - Fine for ocassional usage and one-off situations if you don't work with SVG often.
SVG Directly Inline
Typescript
<?xml version="1.0" encoding="UTF-8"?>
<svg className="h-8 w-8 text-white" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 193.65 211.87">
<path fill="currentColor" d="M187.66,74.62l-5.07-5.76h0s.44-7.58.44-7.58c0-.32-.24-.7-.53-.85s-.74-.11-1,.09l-5.76,4.95h0s-7.66-.54-7.66-.54c-.32,0-.7.23-.84.52-.14.29-.1.73.1.99l5.07,5.76h0s-.44,7.58-.44,7.58c0,.32.24.7.53.85s.74.1,1-.09l5.75-4.94h0s.02,0,.02,0l7.65.54c.32,0,.7-.23.85-.52.14-.29.1-.73-.1-.99Z"/>
<path fill="currentColor" d="M156.77,41.25c-.07.73.37,1.64.99,2.04,0,0,0,0,0,0h0s0,0,0,0c.62.4,1.62.42,2.25.04l14.08-9.65.1-.06h.08s17.03,3.11,17.03,3.11c.73.08,1.63-.35,2.03-.95h.02s0-.01,0-.01c.39-.62.39-1.61.02-2.24l-9.96-14.16-.04-.06v-.12s2.86-16.83,2.86-16.83c.07-.73-.37-1.63-.99-2.02h0s0,0,0,0c0,0,0,0,0,0-.62-.4-1.64-.42-2.27-.05l-14.17,9.72-17.11-3.11c-.73-.08-1.64.35-2.03.96s-.4,1.62-.02,2.25l10.01,14.23-2.87,16.94Z"/>
<path fill="currentColor" d="M75.98,24.75l9.45,4.43,3.56,9.68c.18.4.68.75,1.12.77s.98-.27,1.19-.66l4.49-9.28,9.84-3.48c.4-.17.75-.67.77-1.1s-.27-.96-.66-1.17l-9.45-4.42-3.56-9.68c-.18-.4-.68-.74-1.12-.76s-.98.26-1.19.65l-4.49,9.29-9.84,3.48c-.4.18-.75.67-.77,1.1s.27.97.66,1.17Z"/>
<path fill="currentColor" d="M116.59,6.52l3.14,4.67-1.04,5.46c-.03.24.11.53.31.67s.53.15.73.03l4.65-3.05,5.52,1.12c.23.03.53-.1.66-.3s.14-.52.02-.73l-3.14-4.67,1.04-5.47c.03-.24-.11-.53-.31-.66s-.53-.15-.73-.03l-4.66,3.05-5.52-1.12c-.24-.03-.53.1-.66.3s-.14.52-.02.73Z"/>
<path fill="currentColor" d="M123.76,83.69l-14.09-10.66c-1.94-1.47-4.72-1.08-6.19.86l-10.66,14.09c-1.47,1.94-1.08,4.72.86,6.19l14.09,10.66c1.94,1.47,4.72,1.08,6.19-.86l10.66-14.09c1.47-1.94,1.08-4.72-.86-6.19Z"/>
<path fill="currentColor" d="M175.74,117.48c-6.56-4.96-15.42-4.59-21.58.29l-24.29-18.38,24.09-31.32c5.2-6.75,3.86-16.35-2.98-21.43l-12.38-9.2c-6.84-5.08-16.61-3.72-21.8,3.03l-23.84,30.99-24.33-18.4c3.02-7.25.96-15.89-5.6-20.85-7.78-5.89-18.86-4.35-24.75,3.43-5.89,7.78-4.35,18.86,3.43,24.75,6.56,4.96,15.42,4.59,21.58-.29l17.5,13.24c-15.2-1.63-30.72,2.34-43.28,11.56l-5.6-4.24c-5.83-4.42-14.15-3.26-18.56,2.57l-10.66,14.09c-4.41,5.84-3.26,14.15,2.57,18.56l14.09,10.66c5.84,4.41,14.15,3.26,18.56-2.57l10.66-14.09c4.42-5.84,3.26-14.15-2.57-18.56l-1.06-.8c11.62-7.64,25.78-10.37,39.3-7.74L10.86,178.16c-5.2,6.76-3.86,16.35,2.98,21.43l12.39,9.2c6.84,5.08,16.6,3.72,21.8-3.03l73.17-95.12c6.28,12.34,7.55,26.78,3.33,40.11l-1.06-.8c-5.84-4.41-14.15-3.26-18.56,2.57l-10.66,14.09c-4.41,5.84-3.26,14.15,2.57,18.56l14.09,10.66c5.83,4.41,14.15,3.26,18.56-2.57l10.66-14.09c4.42-5.84,3.26-14.15-2.57-18.56l-5.6-4.24c5.45-14.6,5.06-30.62-.65-44.8l17.5,13.24c-3.02,7.25-.96,15.89,5.6,20.85,7.78,5.89,18.86,4.35,24.75-3.43,5.89-7.78,4.35-18.86-3.43-24.75ZM59.41,51.63c-2.94,3.88-8.49,4.65-12.37,1.72-3.88-2.94-4.65-8.49-1.72-12.37,2.94-3.89,8.49-4.65,12.37-1.72,3.88,2.94,4.65,8.49,1.72,12.37ZM41.52,104.55l-10.66,14.09c-1.47,1.94-4.24,2.33-6.19.86l-14.09-10.66c-1.94-1.47-2.33-4.24-.86-6.19l10.66-14.09c1.47-1.94,4.24-2.33,6.19-.86l14.09,10.66c1.94,1.47,2.33,4.24.86,6.19ZM120.7,109.63c-4.48,5.52-12.54,6.57-18.26,2.25l-14.09-10.66c-5.72-4.32-6.9-12.37-2.8-18.18l8.23-10.95,2.66-3.52c4.41-5.84,12.72-6.99,18.56-2.57l14.09,10.66c5.84,4.41,6.99,12.73,2.57,18.56l-2.66,3.52-8.3,10.89ZM132.25,167.65c1.94,1.47,2.33,4.24.86,6.19l-10.66,14.09c-1.47,1.94-4.24,2.33-6.19.86l-14.09-10.66c-1.94-1.47-2.33-4.24-.86-6.19l10.66-14.09c1.47-1.94,4.24-2.33,6.19-.86l14.09,10.66ZM172.13,136.9c-2.94,3.88-8.49,4.65-12.38,1.72-3.88-2.94-4.65-8.49-1.71-12.37,2.94-3.89,8.49-4.65,12.37-1.72,3.88,2.94,4.65,8.49,1.72,12.37Z"/>
</svg> />
Cons:
- Have to import each
.svg
(or a list of them all pre-defined) inmdx-components
- Adds significant bulk and many lines of code to your
.mdx
- Difficult to read and parse
Pros:
- Fine for ocassional usage and one-off situations if you don't work with SVG often.
- Theming works great once you switch fills to
currentColor
MDX
This one doesn't actually require any different code for MDX from Typescript. SVG is SVG in this case because SVGR/webpack is rendering it for us.
Cons:
- Have to import each
.svg
(or a list of them all pre-defined) inmdx-components
- Adds significant bulk and many lines of code to your
.mdx
- Difficult to read and parse
Pros:
- Fine for ocassional usage and one-off situations if you don't work with SVG often.
- Theming works great once you switch fills to
currentColor
SVG with SVGMagic
<SVGM kind="svgm-mark" className="h-16 text-blue-600 dark:text-blue-400" />
Typescript
- Import once, use over and over again
MDX
- Import one document into
mdx-components
- Extremely readable, maintainable code