How to Build an Icon Library with TypeScript, Tsup, and SVGR

Want to create an icon library for your React project with TypeScript? With Tsup and SVGR, you can efficiently turn SVG files into reusable React components. Let’s go straight to the point and build this from scratch.

Project Folder Struct…


This content originally appeared on DEV Community and was authored by Gustavo Henrique

Want to create an icon library for your React project with TypeScript? With Tsup and SVGR, you can efficiently turn SVG files into reusable React components. Let’s go straight to the point and build this from scratch.

Project Folder Structure

Before diving in, here’s the folder structure we’re aiming for:

my-icons-library/
├── icons/                  # SVG files go here
├── src/
│   └── icons/              # Generated React components
├── scripts/                # Utility scripts for custom tasks
├── index.ts                # Entry point of the library
├── tsconfig.json           # TypeScript config file
├── package.json            # Project settings
└── tsup.config.ts          # Tsup config file for building the project

Step 1: Setting Up the Project

First, create a new folder, initialize the project, and install dependencies:

mkdir my-icons-library
cd my-icons-library
npm init -y
npm install react react-dom typescript tsup @svgr/cli @types/react @types/react-dom svgo --save-dev

This sets up your React environment with TypeScript, SVGR, and Tsup for bundling.

Step 2: Configuring TypeScript

Let’s configure TypeScript. Create a tsconfig.json file and add the following content:

{
  "compilerOptions": {
    "target": "ESNext",
    "module": "ESNext",
    "jsx": "react-jsx",
    "declaration": true,
    "declarationDir": "dist/types",
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true,
    "moduleResolution": "node",
    "skipLibCheck": true,
    "isolatedModules": true
  },
  "include": ["src/**/*", "scripts/*", "index.ts"],
  "exclude": ["node_modules", "dist"]
}

This config ensures TypeScript compiles the icons into proper .d.ts type definitions and outputs the compiled code in the dist folder.

Step 3: Generating Icons with SVGR

Now, let’s turn your SVG files into React components with SVGR. Add the following script to your package.json:

"scripts": {
  "generate-icons": "npx @svgr/cli --typescript --icon --out-dir src/icons icons"
}

Run the command to transform all SVGs in the icons/ folder into React components inside src/icons/:

npm run generate-icons

Step 4: Customizing Icons (Size & Color)

You’ll likely want to modify your icons to allow dynamic sizing and coloring. Create a script called modify-icons.ts in the scripts/ folder:

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const ICONS_DIR = path.join(__dirname, '..', 'src', 'icons');

const modifyIcon = (filePath: string) => {
  let content = fs.readFileSync(filePath, 'utf-8');

  const propsRegex = /SVGProps<SVGSVGElement>/;
  if (propsRegex.test(content)) {
    content = content.replace(
      propsRegex,
      `(SVGProps<SVGSVGElement> & { size?: string | number; color?: string })`
    );
  }

  const svgTagRegex = /<svg([^>]*)>/;
  const svgMatch = content.match(svgTagRegex);
  if (svgMatch) {
    const svgAttributes = svgMatch[1];

    let newSvgAttributes = svgAttributes
      .replace(/width="[^"]*"/, '{...(props.size ? { width: props.size, height: props.size } : { width: "1em", height: "1em" })}')
      .replace(/height="[^"]*"/, '');

    newSvgAttributes += ' fill={props.color || "currentColor"}';

    content = content.replace(svgTagRegex, `<svg${newSvgAttributes}>`);
  }

  fs.writeFileSync(filePath, content, 'utf-8');
};

const main = () => {
  fs.readdir(ICONS_DIR, (err, files) => {
    if (err) {
      console.error('Error reading icons directory:', err);
      process.exit(1);
    }

    files.forEach((file) => {
      const filePath = path.join(ICONS_DIR, file);
      if (path.extname(file) === '.tsx') {
        try {
          modifyIcon(filePath);
          console.log(`Modified: ${file}`);
        } catch (error) {
          console.error(`Error modifying ${file}:`, error);
        }
      }
    });
  });
};

main();

Run this script after generating the icons to add dynamic size and color properties to your components.

Step 5: Automatically Exporting Icons

To avoid manually exporting each icon, create a script called generate-index.ts in the scripts/ folder that will auto-generate an index.ts file for all your icons:

import { readdirSync, writeFileSync } from 'fs';
import { resolve, join, basename } from 'path';

const iconsDir = resolve('src/icons');
const indexPath = join(resolve(), 'index.ts');

const files = readdirSync(iconsDir).filter((file) => file.endsWith('.tsx'));

const exportStatements = files
  .map((file) => {
    const componentName = basename(file, '.tsx');
    return `export { default as ${componentName} } from './src/icons/${componentName}';`;
  })
  .join('\n');

writeFileSync(indexPath, exportStatements);

console.log(`Generated index.ts with ${files.length} icon exports.`);

Run this script to generate the index.ts file with exports for all your icons.

Step 6: Bundling with Tsup

Now it’s time to bundle the project using Tsup. Create a tsup.config.ts file:

import { defineConfig } from 'tsup';

export default defineConfig({
  entry: ['index.ts'],
  outDir: 'dist',
  format: ['cjs', 'esm'],
  dts: true,
  splitting: true,
  sourcemap: true,
  clean: true,
  external: ['react', 'react-dom'],
});

Add the following script to your package.json:

"scripts": {
  "build": "tsup"
}

Run the build command:

npm run build

Your icon library is now bundled and ready to be used!

Step 7: Configuring the package.json

Here’s how your package.json should look like for publishing your library to npm:

{
  "name": "my-icons-library",
  "version": "1.0.0",
  "description": "A TypeScript React icon library",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "files": [
    "dist",
    "src"
  ],
  "scripts": {
    "start": "vite",
    "generate-icons": "npx @svgr/cli --typescript --icon --out-dir src/icons icons",
    "generate-and-modify-icons": "npm run generate-icons && node scripts/modify-icons.ts",
    "build": "tsup",
    "prepublishOnly": "npm run build"
  },
  "peerDependencies": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  },
  "devDependencies": {
    "typescript": "^5.0.0",
    "tsup": "^5.0.0",
    "@svgr/cli": "^5.0.0",
    "@types/react": "^18.0.0",
    "@types/react-dom": "^18.0.0"
  },
  "author": "Your Name",
  "license": "MIT"
}

This package.json setup ensures that your library is packaged correctly and ready for npm publishing.

Step 8: Publishing to npm

Here’s how you can publish your icon library to npm:

1. Create an npm account

If you don’t already have one, create an account on npm.

2. Log in via the terminal

In your project’s root directory, log in to npm:

npm login

3. Publish your package

Once logged in, you can publish your library to npm:

npm publish --access public

This command will upload your package to the npm registry, and it will be available for others to install and use.

Conclusion

By following these steps, you’ve created a fully functional icon library with TypeScript, SVGR, and Tsup. Your icons are now reusable, customizable React components, and you’ve learned how to publish them to npm for the world to use.

I hope you found this guide helpful! If you'd like to see the full repository, feel free to check it out at https://github.com/avoguga/icon-library-template.

For those looking to deepen their understanding of how to create and publish a TypeScript library, I highly recommend checking out the following articles:

Additionally, if you're interested in learning more about how to bundle a tree-shakable TypeScript library using Tsup, I recommend this detailed guide:

These resources will offer you a broader perspective on the best practices and techniques for building and distributing libraries efficiently.


This content originally appeared on DEV Community and was authored by Gustavo Henrique


Print Share Comment Cite Upload Translate Updates
APA

Gustavo Henrique | Sciencx (2024-09-27T21:25:02+00:00) How to Build an Icon Library with TypeScript, Tsup, and SVGR. Retrieved from https://www.scien.cx/2024/09/27/how-to-build-an-icon-library-with-typescript-tsup-and-svgr/

MLA
" » How to Build an Icon Library with TypeScript, Tsup, and SVGR." Gustavo Henrique | Sciencx - Friday September 27, 2024, https://www.scien.cx/2024/09/27/how-to-build-an-icon-library-with-typescript-tsup-and-svgr/
HARVARD
Gustavo Henrique | Sciencx Friday September 27, 2024 » How to Build an Icon Library with TypeScript, Tsup, and SVGR., viewed ,<https://www.scien.cx/2024/09/27/how-to-build-an-icon-library-with-typescript-tsup-and-svgr/>
VANCOUVER
Gustavo Henrique | Sciencx - » How to Build an Icon Library with TypeScript, Tsup, and SVGR. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2024/09/27/how-to-build-an-icon-library-with-typescript-tsup-and-svgr/
CHICAGO
" » How to Build an Icon Library with TypeScript, Tsup, and SVGR." Gustavo Henrique | Sciencx - Accessed . https://www.scien.cx/2024/09/27/how-to-build-an-icon-library-with-typescript-tsup-and-svgr/
IEEE
" » How to Build an Icon Library with TypeScript, Tsup, and SVGR." Gustavo Henrique | Sciencx [Online]. Available: https://www.scien.cx/2024/09/27/how-to-build-an-icon-library-with-typescript-tsup-and-svgr/. [Accessed: ]
rf:citation
» How to Build an Icon Library with TypeScript, Tsup, and SVGR | Gustavo Henrique | Sciencx | https://www.scien.cx/2024/09/27/how-to-build-an-icon-library-with-typescript-tsup-and-svgr/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.