├── .gitignore ├── LICENSE ├── README.md ├── bin ├── build.ts ├── template.ts ├── tsconfig.json └── types.d.ts ├── cleanup.sh ├── compile.js ├── package.json ├── pnpm-lock.yaml └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | solid/ 4 | outline/ 5 | mini/ 6 | micro/ 7 | src/ 8 | build/ 9 | heroicons/ 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 impulse 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://img.shields.io/npm/v/react-native-heroicons.svg)](https://www.npmjs.com/package/react-native-heroicons) 2 | [![npm downloads](https://img.shields.io/npm/dm/react-native-heroicons.svg)](https://www.npmjs.com/package/react-native-heroicons) 3 | 4 | # React Native Heroicons 5 | 6 | Heroicons are designed by [Steve Schoger](https://twitter.com/steveschoger) and published by [Tailwind Labs](https://github.com/tailwindlabs/heroicons). 7 | 8 | A full directory of all available icons can be found here: 9 | 10 | https://heroicons.com/ 11 | 12 | ## Installation 13 | 14 | **react-native-heroicons requires react-native-svg v9 or higher** 15 | 16 | ### yarn 17 | 18 | ```sh 19 | yarn add react-native-heroicons react-native-svg 20 | ``` 21 | 22 | ### npm 23 | 24 | ```sh 25 | npm i react-native-heroicons react-native-svg 26 | ``` 27 | 28 | ## Usage 29 | 30 | Specific icons: 31 | 32 | ```tsx 33 | import React from "react"; 34 | import { View } from "react-native"; 35 | import { SparklesIcon as SparklesIconMicro } from "react-native-heroicons/micro"; 36 | // Old solid style from heroicons v1 37 | import { SparklesIcon as SparklesIconMini } from "react-native-heroicons/mini"; 38 | import { SparklesIcon } from "react-native-heroicons/solid"; 39 | import { SparklesIcon as SparklesIconOutline } from "react-native-heroicons/outline"; 40 | 41 | const App = () => { 42 | return ( 43 | 44 | 45 | 46 | 47 | 48 | 49 | ); 50 | }; 51 | 52 | export default App; 53 | ``` 54 | 55 | Entire icon pack: 56 | 57 | ```tsx 58 | import React from "react"; 59 | import * as Icons from "react-native-heroicons/solid"; 60 | 61 | const App = () => { 62 | return ; 63 | }; 64 | 65 | export default App; 66 | ``` 67 | 68 | ## Customization 69 | 70 | Icons can be adjusted with the `size` prop. 71 | 72 | Defaults are `16` for `micro`, `20` for `mini` and `24` for `solid`/`outline`): 73 | 74 | ```tsx 75 | import { SparklesIcon as SparklesIconOutline } from "react-native-heroicons/outline"; 76 | // ... 77 | ; 78 | ``` 79 | -------------------------------------------------------------------------------- /bin/build.ts: -------------------------------------------------------------------------------- 1 | import { transform } from "@svgr/core"; 2 | import { promises as fs } from "fs"; 3 | import { Template, template16, template20, template24 } from "./template"; 4 | import junk from "junk"; 5 | import camelcase from "camelcase"; 6 | 7 | const ICON_STYLES = ["micro", "mini", "solid", "outline"] as const; 8 | type IconStyle = (typeof ICON_STYLES)[number]; 9 | 10 | const resetSrcDir = async () => { 11 | try { 12 | await fs.rm(`./src`, { recursive: true }); 13 | } catch (error) { 14 | // Allowed to fail 15 | } 16 | try { 17 | await fs.mkdir(`./src`); 18 | ICON_STYLES.forEach(async (style) => { 19 | await fs.mkdir(`./src/${style}`); 20 | }); 21 | } catch (error) { 22 | throw new Error("Failed wiping src folders"); 23 | } 24 | }; 25 | 26 | const genComponentFromBuffer = async ( 27 | componentName: string, 28 | svgBuffer: Buffer, 29 | templateIconSize: 16 | 20 | 24 30 | ): Promise => { 31 | const template = { 32 | 16: template16, 33 | 20: template20, 34 | 24: template24, 35 | }[templateIconSize]; 36 | 37 | try { 38 | return await transform( 39 | svgBuffer, 40 | { 41 | template, 42 | native: true, 43 | typescript: true, 44 | svgProps: { width: "{size}", height: "{size}" }, 45 | svgo: true, 46 | svgoConfig: { 47 | plugins: [ 48 | "removeXMLNS", 49 | { 50 | name: "sortAttrs", 51 | params: { 52 | xmlnOrder: "alphabetical", 53 | }, 54 | }, 55 | { 56 | name: "removeAttrs", 57 | params: { 58 | attrs: ["aria-hidden"], 59 | }, 60 | }, 61 | ], 62 | }, 63 | plugins: [ 64 | "@svgr/plugin-svgo", 65 | "@svgr/plugin-jsx", 66 | "@svgr/plugin-prettier", 67 | ], 68 | }, 69 | { componentName } 70 | ); 71 | } catch (error) { 72 | throw new Error("Failed generating components"); 73 | } 74 | }; 75 | 76 | const getIcons = async (style: IconStyle) => { 77 | const iconDir = "./heroicons/optimized"; 78 | 79 | const stylePath = { 80 | micro: "16/solid", 81 | mini: "20/solid", 82 | outline: "24/outline", 83 | solid: "24/solid", 84 | }[style]; 85 | 86 | let files = await fs.readdir(`${iconDir}/${stylePath}`); 87 | return Promise.all( 88 | files.filter(junk.not).map(async (file) => ({ 89 | svg: await fs.readFile(`${iconDir}/${stylePath}/${file}`), 90 | componentName: `${camelcase(file.replace(/\.svg$/, ""), { 91 | pascalCase: true, 92 | })}Icon`, 93 | })) 94 | ); 95 | }; 96 | 97 | const exportIcons = async (style: IconStyle) => { 98 | const sizeMap: Record = { 99 | micro: 16, 100 | mini: 20, 101 | outline: 24, 102 | solid: 24, 103 | }; 104 | 105 | const icons = await getIcons(style); 106 | for (let { componentName, svg } of icons) { 107 | const jsx = await genComponentFromBuffer( 108 | componentName, 109 | svg, 110 | sizeMap[style] 111 | ); 112 | await fs.writeFile(`./src/${style}/${componentName}.tsx`, jsx); 113 | const exportStr = `export { default as ${componentName} } from './${componentName}';\n`; 114 | await fs.writeFile(`./src/${style}/index.ts`, exportStr, { flag: "a" }); 115 | } 116 | }; 117 | 118 | (async () => { 119 | await resetSrcDir(); 120 | ICON_STYLES.forEach(async (s) => { 121 | await exportIcons(s); 122 | }); 123 | })(); 124 | -------------------------------------------------------------------------------- /bin/template.ts: -------------------------------------------------------------------------------- 1 | import type { Options as TransformOptions } from "@svgr/babel-preset"; 2 | export type Template = TransformOptions["template"]; 3 | 4 | const template16: Template = (variables, { tpl }) => { 5 | return tpl` 6 | import * as React from "react"; 7 | import Svg, { Path, SvgProps, NumberProp } from "react-native-svg"; 8 | 9 | interface Props extends SvgProps { 10 | size?: NumberProp; 11 | } 12 | 13 | const ${variables.componentName} = ({ size = 16, ...props }: Props) => { 14 | return ( 15 | ${variables.jsx} 16 | ) 17 | }; 18 | 19 | ${variables.exports}; 20 | `; 21 | }; 22 | 23 | const template20: Template = (variables, { tpl }) => { 24 | return tpl` 25 | import * as React from "react"; 26 | import Svg, { Path, SvgProps, NumberProp } from "react-native-svg"; 27 | 28 | interface Props extends SvgProps { 29 | size?: NumberProp; 30 | } 31 | 32 | const ${variables.componentName} = ({ size = 20, ...props }: Props) => { 33 | return ( 34 | ${variables.jsx} 35 | ) 36 | }; 37 | 38 | ${variables.exports}; 39 | `; 40 | }; 41 | 42 | const template24: Template = (variables, { tpl }) => { 43 | return tpl` 44 | import * as React from "react"; 45 | import Svg, { Path, SvgProps, NumberProp } from "react-native-svg"; 46 | 47 | interface Props extends SvgProps { 48 | size?: NumberProp; 49 | } 50 | 51 | const ${variables.componentName} = ({ size = 24, ...props }: Props) => { 52 | return ( 53 | ${variables.jsx} 54 | ) 55 | }; 56 | 57 | ${variables.exports}; 58 | `; 59 | }; 60 | 61 | export { template16, template20, template24 }; 62 | -------------------------------------------------------------------------------- /bin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | /* Basic Options */ 4 | // "incremental": true, /* Enable incremental compilation */ 5 | "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 6 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 7 | // "lib": [], /* Specify library files to be included in the compilation. */ 8 | // "allowJs": true, /* Allow javascript files to be compiled. */ 9 | // "checkJs": true, /* Report errors in .js files. */ 10 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ 11 | // "declaration": true, /* Generates corresponding '.d.ts' file. */ 12 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ 13 | // "sourceMap": true, /* Generates corresponding '.map' file. */ 14 | // "outFile": "./", /* Concatenate and emit output to single file. */ 15 | // "outDir": "./", /* Redirect output structure to the directory. */ 16 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 17 | // "composite": true, /* Enable project compilation */ 18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ 19 | // "removeComments": true, /* Do not emit comments to output. */ 20 | // "noEmit": true, /* Do not emit outputs. */ 21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */ 22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ 23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ 24 | 25 | /* Strict Type-Checking Options */ 26 | "strict": true /* Enable all strict type-checking options. */, 27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ 28 | // "strictNullChecks": true, /* Enable strict null checks. */ 29 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */ 30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ 31 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ 32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ 33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 34 | 35 | /* Additional Checks */ 36 | // "noUnusedLocals": true, /* Report errors on unused locals. */ 37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 40 | 41 | /* Module Resolution Options */ 42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 43 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ 44 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ 45 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ 46 | // "typeRoots": [], /* List of folders to include type definitions from. */ 47 | // "types": [], /* Type declaration files to be included in compilation. */ 48 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ 49 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 50 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ 51 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ 52 | 53 | /* Source Map Options */ 54 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ 55 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ 56 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ 57 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ 58 | 59 | /* Experimental Options */ 60 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ 61 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ 62 | 63 | /* Advanced Options */ 64 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /bin/types.d.ts: -------------------------------------------------------------------------------- 1 | declare module "@svgr/core"; 2 | -------------------------------------------------------------------------------- /cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | npm pack --dry-run -------------------------------------------------------------------------------- /compile.js: -------------------------------------------------------------------------------- 1 | const { build } = require("esbuild"); 2 | const glob = require("tiny-glob"); 3 | const fs = require("fs/promises"); 4 | 5 | (async () => { 6 | const entryPoints = ["solid", "outline", "mini", "micro"]; 7 | 8 | entryPoints.forEach(async (ep) => { 9 | const entryPoints = await glob(`./src/${ep}/*.{ts,tsx}`); 10 | 11 | await build({ 12 | entryPoints, 13 | format: "cjs", 14 | minify: true, 15 | outdir: `./${ep}`, 16 | }); 17 | 18 | await build({ 19 | entryPoints, 20 | format: "esm", 21 | minify: true, 22 | outdir: `./${ep}/esm`, 23 | }); 24 | 25 | await fs.writeFile(`./${ep}/package.json`, `{"module": "./esm/index.js"}`); 26 | }); 27 | })(); 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-heroicons", 3 | "version": "4.0.0", 4 | "description": "React Native components for heroicons", 5 | "files": [ 6 | "solid", 7 | "outline", 8 | "mini", 9 | "micro" 10 | ], 11 | "sideEffects": false, 12 | "scripts": { 13 | "fetch": "rimraf heroicons/ && git clone https://github.com/tailwindlabs/heroicons/", 14 | "gen": "npm run fetch && ts-node --project bin/tsconfig.json --files bin/build.ts", 15 | "gen:no": "ts-node --project bin/tsconfig.json --files bin/build.ts", 16 | "cleanup": "./cleanup.sh", 17 | "compile": "rimraf solid && rimraf outline && rimraf mini && rimraf micro && node compile.js && tsc --emitDeclarationOnly --outDir . && cp solid/*.d.ts solid/esm && cp outline/*.d.ts outline/esm && cp mini/*.d.ts mini/esm && cp micro/*.d.ts micro/esm", 18 | "build:fetch": "npm run gen && npm run compile && npm run cleanup", 19 | "build": "npm run compile && npm run cleanup", 20 | "release": "npm run build:fetch && release-it" 21 | }, 22 | "keywords": [ 23 | "react", 24 | "react native", 25 | "heroicons", 26 | "heroiconsui" 27 | ], 28 | "repository": { 29 | "type": "git", 30 | "url": "git+https://github.com/ecklf/react-native-heroicons" 31 | }, 32 | "bugs": { 33 | "url": "https://github.com/ecklf/react-native-heroicons/issues" 34 | }, 35 | "author": "ecklf", 36 | "license": "MIT", 37 | "devDependencies": { 38 | "@svgr/babel-preset": "^8.1.0", 39 | "@svgr/core": "^6.5.1", 40 | "@svgr/plugin-jsx": "^6.5.1", 41 | "@svgr/plugin-prettier": "^6.5.1", 42 | "@svgr/plugin-svgo": "^6.5.1", 43 | "@types/node": "^18.19.3", 44 | "@types/react": "^18.2.45", 45 | "@types/react-native": "^0.70.19", 46 | "camelcase": "^6.3.0", 47 | "esbuild": "^0.13.15", 48 | "junk": "^3.1.0", 49 | "react": "^18.2.0", 50 | "react-native-svg": "^13.14.0", 51 | "release-it": "^15.6.0", 52 | "rimraf": "^3.0.2", 53 | "tiny-glob": "^0.2.9", 54 | "ts-node": "^10.9.1", 55 | "typescript": "^4.9.4" 56 | }, 57 | "peerDependencies": { 58 | "react": ">=16.8", 59 | "react-native-svg": ">=9" 60 | }, 61 | "release-it": { 62 | "git": { 63 | "commitMessage": "chore: release v${version}", 64 | "tagName": "v${version}", 65 | "requireCleanWorkingDir": false 66 | }, 67 | "github": { 68 | "release": true 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, 4 | "module": "ESNext" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, 5 | "jsx": "react-native" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */, 6 | "outDir": "dist" /* Redirect output structure to the directory. */, 7 | "declaration": true /* Generates corresponding '.d.ts' file. */, 8 | "sourceMap": false /* Generates corresponding '.map' file. */, 9 | "strict": true /* Enable all strict type-checking options. */, 10 | "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */, 11 | "allowSyntheticDefaultImports": true /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */, 12 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */, 13 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */, 14 | "isolatedModules": true, 15 | "skipLibCheck": true 16 | }, 17 | "include": ["src/**/*"] 18 | } 19 | --------------------------------------------------------------------------------