So, today I'm here to share about using SVG in React (with Vite).
For those of us who know, or don't know - svg are highly scalable (that's what the S stands for in SVG) vector graphics, and very useful.
they might feel intimidating, but once you get over yourself, they're quite fun, and you keep asking yourself how you ever did without them.
I love using javascript
in conjunction with **typescript**
helps the team develop a common language to work with (interfaces).
I feel that typescript
serves a multitude of purposes, but, let's not forget it's not a real safety net - but more of a convenience. In the real world I'd advocate using something like joi
or zod
or other javascript
type-checking libs.
so here's the stack:
- vite
- react
- typescript
- styled-components
there's a whole list, but we'll focus on these.
my use case is - wanting to create a generic (enough) SVG component, so that anyone trying to implement one - doe's not need to know what is available, but rather have autocompletion.
first, grab some svg's, maybe put them in your public
folder.
make sure to scrub
them down for any fill rules or strokes. makes the overrides easier.
styled-components is nice to have but you could go with scss or vanilla css - to grab nested svg elements.
here's the a breakdown of said component:
so, let's go over what we've done here:
first of all we're importing the SVG's as React components (keep in mind that there are other ways to harness the power of svg in react, as well as vite - that offers us a multitude of solutions I won't delve into in this post.
import { ReactComponent as Avatar } from '/public/avatar.svg';
import { ReactComponent as Dashboard } from '/public/dashboard.svg';
import { ReactComponent as Diamond } from '/public/dimond.svg';
import { ReactComponent as EmptyGames } from '/public/empty-games.svg';
import { ReactComponent as Logo } from '/public/is-logo.svg';
import { ReactComponent as Rocket } from '/public/rocket.svg';
import { ReactComponent as Text } from '/public/text.svg';
import { ReactComponent as NewWindow } from '/public/new-window.svg';
import { ReactComponent as PercentageCake } from '/public/percentage-cake.svg';
import { ReactComponent as NoData } from '/public/no-data.svg';
import { ReactComponent as Menu } from '/public/menu.svg';
import { ReactComponent as Breakdown } from '/public/breakdown.svg';
import { ReactComponent as Profile } from '/public/profile.svg';
import { ReactComponent as ClosedMenu } from '/public/menu-closed.svg';
import { ReactComponent as Collection } from '/public/apps.svg';
import { ReactComponent as Upload } from '/public/upload.svg';
import { ReactComponent as Empty } from '/public/empty.svg';
import { ReactComponent as Cart } from '/public/cart.svg';
import { ReactComponent as Collections } from '/public/collections.svg';
import { ReactComponent as Megaphone } from '/public/megaphone.svg';
import { ReactComponent as Wallet } from '/public/wallet.svg';
import { ReactComponent as Console } from '/public/console.svg';
import { ReactComponent as Back } from '/public/back.svg';
next we're defining a component descriptor type - this is to help us for the SVG Record definition later, basically this is what the value of each key will be holding on that object.
type SVGComponentDescriptor =
React.FunctionComponent<React.SVGProps<SVGSVGElement>
& { title?: string | undefined; }>;
we then proceed to map each key in the SVGKey
type, this will be the actual autocompletion, so no real tricks here.
export type SVGKey =
| 'Avatar'
| 'Collection'
| 'Dashboard'
| 'Upload'
| 'Diamond'
| 'EmptyGames'
| 'Logo'
| 'Rocket'
| 'Text'
| 'NewWindow'
| 'PercentageCake'
| 'NoData'
| 'Menu'
| 'Breakdown'
| 'Profile'
| 'ClosedMenu'
| 'Wallet'
| 'Cart'
| 'Collections'
| 'Megaphone'
| 'Console'
| 'Back'
| 'Empty';
next, we'll define the props for the component, as you can see, it's a merge of the custom type
prop, i've added, and react-typescript's own definition of what SVG props should look like on an svg element.
type SVGProps = { type: SVGKey; } & React.SVGProps<SVGSVGElement>;
next up we have the SVG object, to be accessed by key-value pairs.
export const SVG: Record<string, SVGComponentDescriptor> = {
Avatar,
Collection,
Dashboard,
Upload,
Diamond,
EmptyGames,
Logo,
Rocket,
Text,
NewWindow,
PercentageCake,
NoData,
Menu,
Breakdown,
Profile,
ClosedMenu,
Empty,
Megaphone,
Wallet,
Cart,
Collections,
Console,
Back,
};
last, we have the exported component:
export default function FlameSVG(props: SVGProps) {
const { type, ...otherProps } = props;
const SVGComponent = SVG[type];
return <SVGComponent {...otherProps} />;
so, what we've done here is accept a merged set of props, made up from react implementation of the DOM svg props, and our own custom type.
we then "pluck" out the type
prop, so that we can use it, as well as not propagate it to the DOM element which does not know this prop (react should throw an error if you do), and use the rest
operator for the rest of the props.
this mean's we have full autocompletion for any svg props we'd like to use, as well as the type
props.
we then access the SVG object Record we've created before, and it returns us a JSX SVG element (not really JSX, but I can't remember the proper definition at this moment).
that is what we return, in addition, we spread
out the DOM props, that might have been used into the svg component. and voila ! auto completion, of both native props, as well as control over what svg will be returned in the DOM.
hope you found this useful, try It out, let me know what you think.
Top comments (0)