npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-dynamic-svg-icon

v1.0.0

Published

A lightweight, cross-platform dynamic SVG icon wrapper for React Web, Expo, and React Native CLI.

Readme

⚛️ react-dynamic-svg-icon

A lightweight, zero-friction, cross-platform dynamic SVG icon wrapper for React Web (Vite), Expo, and React Native CLI.

Building cross-platform apps often means fighting with SVG configurations. Web needs vite-plugin-svgr and CSS color properties. React Native needs react-native-svg-transformer and explicit color props.

react-dynamic-svg-icon solves this. It provides a single, unified <DynamicIcon> component that normalizes props across all platforms, and includes 1-liner configuration wrappers so you never have to write complex bundler boilerplate again.

✨ Features

  • Write Once, Run Anywhere: Use the exact same icon component on Web, iOS, and Android.
  • Unified Sizing: Pass a single size={24} prop instead of juggling width and height.
  • Smart Styling: Automatically maps the color prop to Web CSS or React Native props safely.
  • Test Ready: Pass testID="my-icon" and it automatically maps to data-testid on the web and testID on native.
  • Zero Boilerplate: Includes built-in plugins for Vite and Metro to handle SVGO optimization safely (preserves your masks and gradients!).

📦 Installation

Install the package and its peer dependencies based on your environment.

npm install react-dynamic-svg-icon

For Web (Vite):

npm install -D vite-plugin-svgr

For React Native / Expo:

npm install react-native-svg
npm install -D react-native-svg-transformer

⚙️ Configuration (1-Line Setup)

We handle the heavy lifting for your bundler so you don't have to.

Web (Vite)

Open your vite.config.ts and add the dynamicSvgPlugin:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { dynamicSvgPlugin } from 'react-dynamic-svg-icon/vite';

export default defineConfig({
  plugins: [
    react(),
    dynamicSvgPlugin() // Automatically configures SVGR safely!
  ],
});

React Native / Expo (Metro)

Open your metro.config.js and wrap your default config with withDynamicSvg.

Standard Setup:

const { getDefaultConfig } = require("expo/metro-config"); // or '@react-native/metro-config'
const { withDynamicSvg } = require('react-dynamic-svg-icon/metro');

const config = getDefaultConfig(__dirname);

module.exports = withDynamicSvg(config);

Advanced Setup (Chaining with NativeWind / Reanimated):

If you are using other tools that require Metro wrappers (like NativeWind), simply chain them by reassigning the config object:

const { getDefaultConfig } = require("expo/metro-config");
const { withNativeWind } = require('nativewind/metro');
const { withDynamicSvg } = require('react-dynamic-svg-icon/metro');

let config = getDefaultConfig(__dirname);

// 1. Apply NativeWind
config = withNativeWind(config, { input: './global.css' });

// 2. Apply Dynamic SVG
config = withDynamicSvg(config);

module.exports = config;

🛠️ The "Golden Rule" for SVG Files

For this package to dynamically change the colors of your icons, your raw .svg files must use currentColor instead of hardcoded hex codes.

  • Bad (Hardcoded): <path fill="#FF0000" d="..." />
  • Good (Dynamic): <path fill="currentColor" d="..." />

Pro-Tip: When exporting icons from Figma, change your icon color to black (#000000), export the SVG, and do a quick "Find and Replace" in your code editor to swap #000000 with currentColor.

🚀 Usage

Example 1: Web (React + Tailwind)

import React, { useState } from 'react';
import HomeIcon from './assets/home.svg'; 
import { DynamicIcon } from 'react-dynamic-svg-icon';

export default function App() {
  const [isActive, setIsActive] = useState(false);

  return (
    <button onClick={() => setIsActive(!isActive)}>
      <DynamicIcon 
        Icon={HomeIcon} 
        size={32} 
        color={isActive ? "blue" : "gray"} 
        strokeWidth={isActive ? 2 : 1}
        className="hover:scale-110 transition-transform"
      />
    </button>
  );
}

Example 2: React Native / Expo

import React, { useState } from 'react';
import { TouchableOpacity, View } from 'react-native';
import HomeIcon from './assets/home.svg'; 
import { DynamicIcon } from 'react-dynamic-svg-icon';

export default function MobileApp() {
  const [isActive, setIsActive] = useState(false);

  return (
    <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
      <TouchableOpacity onPress={() => setIsActive(!isActive)}>
        <DynamicIcon 
          Icon={HomeIcon} 
          size={40} 
          color={isActive ? "#3B82F6" : "#9CA3AF"} 
          testID="home-tab-icon"
        />
      </TouchableOpacity>
    </View>
  );
}

📖 Props API

| Prop | Type | Default | Description | | :--- | :--- | :--- | :--- | | Icon (Required) | ComponentType | undefined | The imported .svg asset. | | size | number \| string | 24 | Unified shortcut applied to both width and height. | | color | string | undefined | Global color override. Drives the currentColor value in the SVG. | | fill | string | undefined | Explicit fill color. Overrides the SVG's internal fill. | | stroke | string | undefined | Explicit stroke color. Overrides the SVG's internal stroke. | | strokeWidth| number \| string | undefined | Thickness of the stroke paths. | | opacity | number \| string | undefined | Global opacity of the icon (0 to 1). | | className | string | undefined | Utility class string (e.g., Tailwind). Safely ignored on Native. | | style | CSSProperties | undefined | Inline styles. Accepts Web CSS and React Native style objects. | | testID | string | undefined | Testing identifier. Auto-maps to data-testid on Web and testID on Native. |

(Supports all other standard SVG props like strokeLinecap, strokeLinejoin, fillOpacity, etc.)

⚠️ Troubleshooting

My icon color isn't changing!

  • Ensure your .svg file uses currentColor instead of a hardcoded hex code like fill="white".
  • Vite Cache (Web): If you just updated your SVG file, Vite might be caching the old version. Run your dev server with npm run dev -- --force or delete the node_modules/.vite folder to bust the cache.
  • Metro Cache (Native): Start your Expo/Native server with npx expo start -c to clear the Metro bundler cache.

💖 Support the Project

If this package saved you hours of configuring Webpack, Vite, and Metro bundlers, consider supporting my open-source work!

Sponsor on GitHub

License: MIT