
Product
Announcing Precomputed Reachability Analysis in Socket
Socket’s precomputed reachability slashes false positives by flagging up to 80% of vulnerabilities as irrelevant, with no setup and instant results.
@neodx/svg
Advanced tools
Supercharge your icons ⚡️
Sprites are the most effective way to work with your SVG icons, but for some reason developers (vision from react world) prefer mostly bloated and ineffective - "compile" SVG to react component with inlined SVG content.
Of course, we can use some external tools like https://svgsprit.es/ or some npm libraries, but that's not serious (if you know any alternatives - let me know, and I'll add links), developers need DX.
In a ridiculous, but incredibly popular way, we don't have other solutions with the same DX.
Just think about it a little, you need to "compile" SVG, to embed your secondary static content in JSX and get a lot of unwanted issues: additional source code, extra build time, extra bundle size, the user's browser will parse and evaluate your static SVG as JS code, you can never cache it, WOOF, etc., etc.
And yes, developers keep using this insanity because even an incredibly inefficient solution with a good DX is better than a super-efficient, but unusable setup with semi-manual generators.
That's why we're here! 🥳
# npm
npm install -D @neodx/svg
# yarn
yarn add -D @neodx/svg
# pnpm
pnpm add -D @neodx/svg
You can try it even without any configuration, just run sprite
command:
yarn sprite
It will search for all SVG files except files in public/sprites
folder and generate sprites in public/sprites
.
In default mode, it will be the single sprite with all icons without grouping and TS definitions, but you can customize it, see below.
option | default | description |
---|---|---|
-i , --input | "**/*.svg" | Glob paths to icons files (output path will be automatically excluded) |
-o , --output | "public/sprites" | Base path to generated sprite/sprites folder |
-d , --definitions | Not provided (disabled) | Path to generated TS file with sprite meta |
--root | "." (same as the current dir) | Base path to your assets, useful for correct groups names careful: --input should be relative to --root |
--group | false | Should we group icons by folders? |
--dry-run | false | Print proposal of generated file paths without actually generating it |
--optimize | true | Should we optimize SVG with svgo? |
--reset-color-values | "#000,#000000" | An array of colors to replace as currentColor |
--reset-color-properties | "fill,stroke" | An array of SVG properties that will be replaced with currentColor if they're present |
Note:
--reset-color-values
and--reset-color-properties
are strings with comma-separated values, don't forget to wrap them with quotes:
sprite ... --reset-color-values "#000,#000000,#fff"
Let's run sprite
with some additional options:
yarn sprite --group --root assets -o public/sprite -d src/shared/ui/icon/sprite.h.ts
As a result, we will get:
...
shared/
ui/
icon/
+ sprite.h.ts
public/
+ sprite/
+ common.svg
+ other.svg
assets/
common/
add.svg
close.svg
other/
cut.svg
search.svg
In details:
--group
option we will group icons by folders (common
and other
)--root
option we will use assets
as a base path for icons (you can try to remove it and see the difference)-o
option we will use public/sprite
as a base path for generated sprites (it's default value, but let's keep it for now)-d
option we will generate TS definitions file with sprite metaexport interface SpritesMap {
common: 'close' | 'favourite';
format: 'align-left' | 'tag';
}
export const SPRITES_META: { [K in keyof SpritesMap]: SpritesMap[K][] } = {
common: ['close', 'favourite'],
format: ['align-left', 'tag']
};
As you can see, we have a map of all sprites and meta information about them.
Now we can use it in our code - for type checking, autocomplete and other cool stuff.
It's a simple implementation, you can see a more real one in the "Recipes" section
// shared/ui/icon/icon.tsx
import { SpritesMap } from './sprite-definitions';
export interface IconProps<Group extends keyof SpritesMap> {
name: SpritesMap[Group];
type?: Group;
}
export function Icon<Group extends keyof SpritesMap = 'common'>({ type, name }: IconProps<Group>) {
return (
<svg className="icon">
<use xlinkHref={`/public/sprite/${type}.svg#${name}`}></use>
</svg>
);
}
import { Icon, TextField } from '@/shared/ui';
export function SomeFeature() {
return (
<div className="space-y-4">
<TextField name="a" startNode={<Icon name="add" />} />
<TextField name="b" startNode={<Icon name="close" />} />
<TextField name="c" startNode={<Icon type="other" name="search" />} />
</div>
);
}
// shared/ui/icon/icon.tsx
import clsx from 'clsx';
import { SVGProps, ForwardedRef, forwardRef } from 'react';
import { SpritesMap } from './sprite-definitions';
export type SpriteKey = {
[Key in keyof SpritesMap]: `${Key}/${SpritesMap[Key]}`;
}[keyof SpritesMap];
export interface IconProps extends Omit<SVGProps<SVGSVGElement>, 'name' | 'type'> {
name: SpriteKey;
}
export function Icon({ name, className, viewBox, ...props }: IconProps) {
const [spriteName, iconName] = name.split('/');
return (
<svg
className={clsx(
'select-none fill-current w-[1em] h-[1em] inline-block text-inherit',
className
)}
viewBox={viewBox}
focusable="false"
aria-hidden
{...props}
>
<use xlinkHref={`/path/to/sprites/${spriteName}.svg#${iconName}`} />
</svg>
);
}
import { Icon } from '@/shared/ui';
export function SomeFeature() {
return (
<div className="space-y-4">
<Icon name="common/add" />
<Icon name="common/close" />
<Icon name="other/search" />
</div>
);
}
FAQs
Supercharge your icons ⚡️
We found that @neodx/svg demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket’s precomputed reachability slashes false positives by flagging up to 80% of vulnerabilities as irrelevant, with no setup and instant results.
Product
Socket is launching experimental protection for Chrome extensions, scanning for malware and risky permissions to prevent silent supply chain attacks.
Product
Add secure dependency scanning to Claude Desktop with Socket MCP, a one-click extension that keeps your coding conversations safe from malicious packages.