SVG Custom Componet
Published: 2022-09-19 18:11:21

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.

```typescript
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.

```react
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.

```react
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.

```react
type SVGProps = { type: SVGKey; } & React.SVGProps<SVGSVGElement>;
```

next up we have the SVG object, to be accessed by key-value pairs.

```react
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:

```typescript
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.