@shaquillehinds/react-native-svg-icons 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +347 -491
- package/bin/find-icon.js +248 -0
- package/bin/install-rules.js +146 -0
- package/package.json +7 -1
- package/rules/AGENT_RULES.md +353 -0
package/README.md
CHANGED
|
@@ -1,470 +1,346 @@
|
|
|
1
|
-
# react-native-svg-icons
|
|
1
|
+
# @shaquillehinds/react-native-svg-icons
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A type-safe SVG icon set for React Native. 997 icons in each of two variants —
|
|
4
|
+
`filled` and `outline` — with icon names validated at compile time and built-in
|
|
5
|
+
path animation.
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
<img src="https://raw.githubusercontent.com/shaquillehinds/react-native-svg-icons/master/assets/svgicons.gif" alt="example" height="500"/>
|
|
7
|
-
</p>
|
|
8
|
-
|
|
9
|
-
## Features
|
|
7
|
+
---
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
- 🔒 **Fully Type-Safe** - TypeScript-first with auto-generated types ensuring icon names are validated at compile time
|
|
13
|
-
- 🎭 **Dual Variants** - Every icon available in both `filled` and `outline` styles
|
|
14
|
-
- ⚡ **Performance Optimized** - Tree-shakeable with efficient SVG rendering via `react-native-svg`
|
|
15
|
-
- 🎨 **Highly Customizable** - Control size, color, and pass custom SVG/Path props
|
|
16
|
-
- ✨ **Built-in Animations** - Powerful animation support with two modes: interpolation-based and property-based animations
|
|
17
|
-
- 🔧 **Zero Configuration** - Works out of the box with sensible defaults
|
|
9
|
+
## Contents
|
|
18
10
|
|
|
19
|
-
|
|
11
|
+
- [Installation](#installation)
|
|
12
|
+
- [Quick start](#quick-start)
|
|
13
|
+
- [Finding icon names](#finding-icon-names)
|
|
14
|
+
- [Props](#props)
|
|
15
|
+
- [Filled vs outline](#filled-vs-outline)
|
|
16
|
+
- [TypeScript](#typescript)
|
|
17
|
+
- [Animation](#animation)
|
|
18
|
+
- [Bundle size](#bundle-size)
|
|
19
|
+
- [Recipes](#recipes)
|
|
20
|
+
- [Troubleshooting](#troubleshooting)
|
|
21
|
+
- [AI agent rules](#ai-agent-rules)
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
npm install react-native-svg-icons react-native-svg @shaquillehinds/react-native-essentials
|
|
23
|
-
```
|
|
23
|
+
---
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
## Installation
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
|
|
28
|
+
npm install @shaquillehinds/react-native-svg-icons react-native-svg @shaquillehinds/react-native-essentials
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
`react-native-svg` and `@shaquillehinds/react-native-essentials` are peer
|
|
32
|
+
dependencies.
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
For iOS, you'll need to install pods:
|
|
34
|
+
iOS:
|
|
36
35
|
|
|
37
36
|
```bash
|
|
38
37
|
cd ios && pod install
|
|
39
38
|
```
|
|
40
39
|
|
|
41
|
-
|
|
40
|
+
---
|
|
42
41
|
|
|
43
|
-
|
|
42
|
+
## Quick start
|
|
44
43
|
|
|
45
44
|
```tsx
|
|
46
|
-
import { SvgIcon } from 'react-native-svg-icons';
|
|
45
|
+
import { SvgIcon } from '@shaquillehinds/react-native-svg-icons';
|
|
47
46
|
|
|
48
47
|
function MyComponent() {
|
|
49
|
-
return
|
|
48
|
+
return (
|
|
49
|
+
<>
|
|
50
|
+
<SvgIcon type="filled" name="Heart" size={24} color="#FF0000" />
|
|
51
|
+
<SvgIcon type="outline" name="SearchNormal" size={20} color="#8E8E93" />
|
|
52
|
+
</>
|
|
53
|
+
);
|
|
50
54
|
}
|
|
51
55
|
```
|
|
52
56
|
|
|
53
|
-
|
|
57
|
+
`type` and `name` are required. `type` also selects which name union `name` is
|
|
58
|
+
checked against, so an outline-only name on `type="filled"` is a compile error.
|
|
54
59
|
|
|
55
|
-
|
|
56
|
-
<SvgIcon type="outline" name="Heart" size={24} color="#000000" />
|
|
57
|
-
```
|
|
60
|
+
---
|
|
58
61
|
|
|
59
|
-
|
|
62
|
+
## Finding icon names
|
|
60
63
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
size={32}
|
|
66
|
-
color="#FFD700"
|
|
67
|
-
svgProps={{
|
|
68
|
-
opacity: 0.8,
|
|
69
|
-
style: { marginRight: 8 },
|
|
70
|
-
}}
|
|
71
|
-
/>
|
|
72
|
-
```
|
|
64
|
+
Names are irregular. Some concepts have no obvious name (`Search` does not exist;
|
|
65
|
+
`SearchNormal` does), some families carry misspellings that are part of the real
|
|
66
|
+
name, and a handful of names start with a lowercase letter or a digit. Look names
|
|
67
|
+
up rather than guessing.
|
|
73
68
|
|
|
74
|
-
###
|
|
69
|
+
### CLI
|
|
75
70
|
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
strokeWidth: 2,
|
|
84
|
-
strokeLinecap: 'round',
|
|
85
|
-
}}
|
|
86
|
-
/>
|
|
71
|
+
```sh
|
|
72
|
+
npx rnsi-icons search # fuzzy match across both variants
|
|
73
|
+
npx rnsi-icons arrow --type outline # restrict to one variant
|
|
74
|
+
npx rnsi-icons Trash --exact # confirm a single name
|
|
75
|
+
npx rnsi-icons --diff # names missing from one variant
|
|
76
|
+
npx rnsi-icons --list --shared # every name present in both
|
|
77
|
+
npx rnsi-icons search --json # machine-readable
|
|
87
78
|
```
|
|
88
79
|
|
|
89
|
-
|
|
80
|
+
`--limit N` caps output (default 40; `--limit 0` for everything).
|
|
90
81
|
|
|
91
|
-
###
|
|
82
|
+
### Editor
|
|
92
83
|
|
|
93
|
-
|
|
84
|
+
`name` is a string-literal union, so autocomplete lists valid names inline and
|
|
85
|
+
TypeScript rejects anything else. Do not silence that with a cast.
|
|
94
86
|
|
|
95
|
-
|
|
87
|
+
### Names people expect that do not exist
|
|
96
88
|
|
|
97
|
-
|
|
|
98
|
-
|
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
89
|
+
| Expected | Actual |
|
|
90
|
+
| -------------------- | ---------------------------------------------- |
|
|
91
|
+
| `Search` | `SearchNormal`, `SearchStatus`, `SearchZoomIn` |
|
|
92
|
+
| `Delete` | `Trash` |
|
|
93
|
+
| `Loading`, `Spinner` | `Refresh`, `RotateLeft`, `RotateRight` |
|
|
94
|
+
| `Warning` | `Warning2` |
|
|
95
|
+
| `Info` | `InfoCircle`, `Information` |
|
|
96
|
+
| `Close` | `CloseCircle`, `CloseSquare` |
|
|
97
|
+
| `Bitcoin` | `BitcoinBtc`, `BitcoinCard`, `BitcoinConvert` |
|
|
106
98
|
|
|
107
|
-
###
|
|
99
|
+
### Misspellings that are part of the name
|
|
108
100
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
FilledIconName, // Union type of all filled icon names
|
|
112
|
-
OutlineIconName, // Union type of all outline icon names
|
|
113
|
-
IconName, // Union of both filled and outline names
|
|
114
|
-
SvgIconType, // 'filled' | 'outline'
|
|
115
|
-
SvgIconProps, // Component props type
|
|
116
|
-
} from 'react-native-svg-icons';
|
|
117
|
-
```
|
|
101
|
+
These are the correct spellings as shipped. The "corrected" version will not
|
|
102
|
+
compile:
|
|
118
103
|
|
|
119
|
-
|
|
104
|
+
`MinusCirlce` · `UserCirlceAdd` · `SendSqaure2` · `MonitorMobbile` ·
|
|
105
|
+
`Battery3full` · `BrifecaseCross` · `BrifecaseTick` · `BrifecaseTimer`
|
|
120
106
|
|
|
121
|
-
|
|
107
|
+
`Briefcase` on its own is spelled normally; only its compounds are not.
|
|
122
108
|
|
|
123
|
-
###
|
|
109
|
+
### Other conventions
|
|
124
110
|
|
|
125
|
-
-
|
|
126
|
-
|
|
127
|
-
-
|
|
128
|
-
|
|
129
|
-
-
|
|
130
|
-
- **Media**: Play, Pause, Stop, Record, Camera, Video, Music
|
|
131
|
-
- **Communication**: Message, Call, Email, Notification, Chat
|
|
132
|
-
- **Files & Folders**: Document, Folder, Archive, Cloud, Download, Upload
|
|
133
|
-
- **Commerce**: Shopping bag, cart, wallet, card, tag
|
|
134
|
-
- **User & Profile**: User, Profile, Avatar, Account, Team
|
|
135
|
-
- **Time & Calendar**: Clock, Calendar, Timer, Alarm
|
|
136
|
-
- **Location**: Map, Location, GPS, Compass, Globe
|
|
137
|
-
- **Weather**: Sun, Moon, Cloud, Rain, Storm
|
|
138
|
-
- **Security**: Lock, Unlock, Shield, Key, Eye, Scan
|
|
139
|
-
- **Devices**: Mobile, Tablet, Desktop, Laptop, Watch
|
|
140
|
-
- **And many more...**
|
|
111
|
+
- Numeric suffixes mark variants, not sizes — `Home`, `Home1`, `Home2` and
|
|
112
|
+
`HomeHashtag` are four distinct icons.
|
|
113
|
+
- A few names begin lowercase or with a digit: `square`, `dcube`, `dRotate`,
|
|
114
|
+
`dSquare`, `dCubeScan`, `4Support`.
|
|
115
|
+
- `4Support` is the name to pass, even though its source file is `FourSupport.tsx`.
|
|
141
116
|
|
|
142
|
-
###
|
|
117
|
+
### Variant coverage
|
|
143
118
|
|
|
144
|
-
|
|
145
|
-
// UI Elements
|
|
146
|
-
('Add',
|
|
147
|
-
'AddCircle',
|
|
148
|
-
'AddSquare',
|
|
149
|
-
'Remove',
|
|
150
|
-
'Edit',
|
|
151
|
-
'Delete',
|
|
152
|
-
'Search',
|
|
153
|
-
'Filter');
|
|
154
|
-
|
|
155
|
-
// Arrows
|
|
156
|
-
('ArrowUp',
|
|
157
|
-
'ArrowDown',
|
|
158
|
-
'ArrowLeft',
|
|
159
|
-
'ArrowRight',
|
|
160
|
-
'ArrowChevronUp',
|
|
161
|
-
'ArrowCircleDown');
|
|
162
|
-
|
|
163
|
-
// Social Media
|
|
164
|
-
('Facebook', 'Instagram', 'Twitter', 'Youtube', 'Linkedin', 'Tiktok');
|
|
165
|
-
|
|
166
|
-
// Crypto
|
|
167
|
-
('Bitcoin', 'Ethereum', 'Binance', 'Cardano', 'Polygon', 'Solana');
|
|
168
|
-
|
|
169
|
-
// Media
|
|
170
|
-
('Play',
|
|
171
|
-
'Pause',
|
|
172
|
-
'Stop',
|
|
173
|
-
'Record',
|
|
174
|
-
'VolumeUp',
|
|
175
|
-
'VolumeDown',
|
|
176
|
-
'Camera',
|
|
177
|
-
'Video');
|
|
178
|
-
|
|
179
|
-
// Communication
|
|
180
|
-
('Message',
|
|
181
|
-
'Messages',
|
|
182
|
-
'Call',
|
|
183
|
-
'Calling',
|
|
184
|
-
'Sms',
|
|
185
|
-
'Notification',
|
|
186
|
-
'NotificationBing');
|
|
187
|
-
|
|
188
|
-
// Files
|
|
189
|
-
('Document', 'Folder', 'FolderOpen', 'Archive', 'CloudAdd', 'CloudDownload');
|
|
190
|
-
|
|
191
|
-
// Commerce
|
|
192
|
-
('Bag',
|
|
193
|
-
'ShoppingBag',
|
|
194
|
-
'ShoppingCart',
|
|
195
|
-
'Wallet',
|
|
196
|
-
'Card',
|
|
197
|
-
'Tag',
|
|
198
|
-
'TicketDiscount');
|
|
199
|
-
|
|
200
|
-
// User
|
|
201
|
-
('User', 'Profile', 'ProfileCircle', 'People', 'UserAdd', 'UserRemove');
|
|
202
|
-
|
|
203
|
-
// Time
|
|
204
|
-
('Clock', 'Calendar', 'Timer', 'Alarm', 'Watch', 'TimeCircle');
|
|
205
|
-
|
|
206
|
-
// Location
|
|
207
|
-
('Location', 'Map', 'Gps', 'DirectionRight', 'GlobalSearch', 'RouteSquare');
|
|
208
|
-
|
|
209
|
-
// Security
|
|
210
|
-
('Lock', 'Unlock', 'Shield', 'ShieldTick', 'Eye', 'EyeSlash', 'Scan', 'Key');
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
## TypeScript Support
|
|
119
|
+
994 names exist in both variants. Three do not:
|
|
214
120
|
|
|
215
|
-
|
|
121
|
+
| `filled` only | `outline` only |
|
|
122
|
+
| ----------------------- | ----------------------- |
|
|
123
|
+
| `ArrowPointCircleup` | `ArrowPointCircleUp` |
|
|
124
|
+
| `ArrowPointCircleRight` | `ArrowPointCircleright` |
|
|
125
|
+
| `FlashCircle` | `FlashCircle2` |
|
|
216
126
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
<SvgIcon type="filled" name="Heart" />
|
|
127
|
+
Anything that switches `type` on a fixed name — a focused tab icon, a
|
|
128
|
+
pressed-state toggle — should avoid these six.
|
|
220
129
|
|
|
221
|
-
|
|
222
|
-
<SvgIcon type="outline" name="Heart" />
|
|
130
|
+
---
|
|
223
131
|
|
|
224
|
-
|
|
225
|
-
|
|
132
|
+
## Props
|
|
133
|
+
|
|
134
|
+
| Prop | Type | Default | Description |
|
|
135
|
+
| ----------- | ------------------------------ | ----------- | ------------------------------------------------ |
|
|
136
|
+
| `type` | `'filled' \| 'outline'` | required | Variant, and the union `name` is checked against |
|
|
137
|
+
| `name` | `IconNameByType[T]` | required | Icon name |
|
|
138
|
+
| `size` | `number` | `24` | Width and height, normalised for the device |
|
|
139
|
+
| `color` | `string` | `'#292D32'` | `fill` on filled icons, `stroke` on outline |
|
|
140
|
+
| `svgProps` | `SvgProps` | – | Spread onto the `<Svg>` element |
|
|
141
|
+
| `pathProps` | `PathProps` | – | Spread onto every `<Path>` in the icon |
|
|
142
|
+
| `animate` | `AnimateSVGPathComponentProps` | – | See [Animation](#animation) |
|
|
143
|
+
|
|
144
|
+
Three details worth knowing before you reach for `svgProps` or `pathProps`:
|
|
145
|
+
|
|
146
|
+
- **`size` is normalised.** It runs through `normalize()` from
|
|
147
|
+
`@shaquillehinds/react-native-essentials`, which scales against device
|
|
148
|
+
dimensions. Treat `size` as a design-scale value rather than exact points, and
|
|
149
|
+
don't compute it from `Dimensions` yourself.
|
|
150
|
+
- **`pathProps` overrides `color`.** It is spread after the fill/stroke
|
|
151
|
+
attribute. Same for `svgProps` and `size` — `svgProps={{ width: 40 }}` wins.
|
|
152
|
+
Prefer `color` and `size`.
|
|
153
|
+
- **`pathProps` hits every path.** Outline icons are usually several paths. There
|
|
154
|
+
is no per-path styling through this API.
|
|
155
|
+
|
|
156
|
+
The default colour is `#292D32`, a near-black grey. If you need true black, pass
|
|
157
|
+
`color="#000"` explicitly.
|
|
226
158
|
|
|
227
|
-
|
|
228
|
-
function IconWrapper<T extends SvgIconType>(props: SvgIconProps<T>) {
|
|
229
|
-
return <SvgIcon {...props} />;
|
|
230
|
-
}
|
|
231
|
-
```
|
|
159
|
+
---
|
|
232
160
|
|
|
233
|
-
##
|
|
161
|
+
## Filled vs outline
|
|
234
162
|
|
|
235
|
-
The
|
|
163
|
+
The two variants are drawn differently, which changes what you can override and
|
|
164
|
+
animate.
|
|
236
165
|
|
|
237
|
-
|
|
166
|
+
| | `filled` | `outline` |
|
|
167
|
+
| ------------ | -------- | --------------- |
|
|
168
|
+
| Paths | One | Usually several |
|
|
169
|
+
| Coloured via | `fill` | `stroke` |
|
|
170
|
+
| Stroke width | – | `1.5` |
|
|
171
|
+
| SVG `fill` | `none` | `none` |
|
|
238
172
|
|
|
239
|
-
|
|
173
|
+
Consequences:
|
|
240
174
|
|
|
241
|
-
|
|
175
|
+
- `pathProps={{ fill: 'red' }}` does nothing to an outline icon.
|
|
176
|
+
- `pathProps={{ stroke: 'red' }}` does nothing to a filled icon.
|
|
177
|
+
- Stroke and dash animations only make sense on `outline`; fill animations on
|
|
178
|
+
`filled`.
|
|
179
|
+
- Adjust outline weight with `pathProps={{ strokeWidth: 2 }}`.
|
|
242
180
|
|
|
243
|
-
|
|
181
|
+
`color` targets the right attribute for the variant automatically — prefer it.
|
|
244
182
|
|
|
245
|
-
|
|
246
|
-
- Support for multiple animation stages
|
|
247
|
-
- Full control over timing, spring animations, and easing
|
|
183
|
+
---
|
|
248
184
|
|
|
249
|
-
|
|
185
|
+
## TypeScript
|
|
250
186
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
187
|
+
```tsx
|
|
188
|
+
import type {
|
|
189
|
+
FilledIconName, // union of all filled names
|
|
190
|
+
OutlineIconName, // union of all outline names
|
|
191
|
+
IconName, // both
|
|
192
|
+
SvgIconType, // 'filled' | 'outline'
|
|
193
|
+
SvgIconProps, // component props, generic over type
|
|
194
|
+
SvgIcon as SvgIconBaseProps,
|
|
195
|
+
} from '@shaquillehinds/react-native-svg-icons';
|
|
196
|
+
```
|
|
257
197
|
|
|
258
|
-
|
|
198
|
+
### Annotate icon arrays
|
|
259
199
|
|
|
260
200
|
```tsx
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
autoStart: true,
|
|
268
|
-
loop: -1,
|
|
269
|
-
returnToStart: true,
|
|
270
|
-
pathProps: (value, { inputRange }) => ({
|
|
271
|
-
strokeLinejoin: 'miter',
|
|
272
|
-
stroke: value.interpolate({
|
|
273
|
-
inputRange,
|
|
274
|
-
outputRange: ['red', 'green'],
|
|
275
|
-
}),
|
|
276
|
-
strokeDashoffset: value.interpolate({
|
|
277
|
-
inputRange,
|
|
278
|
-
outputRange: [36, 0],
|
|
279
|
-
}),
|
|
280
|
-
strokeDasharray: '35, 35',
|
|
281
|
-
}),
|
|
282
|
-
animationConfig: [
|
|
283
|
-
{
|
|
284
|
-
type: 'timing',
|
|
285
|
-
duration: 1000,
|
|
286
|
-
useNativeDriver: true,
|
|
287
|
-
},
|
|
288
|
-
],
|
|
289
|
-
}}
|
|
290
|
-
/>
|
|
201
|
+
const icons: OutlineIconName[] = [
|
|
202
|
+
'House',
|
|
203
|
+
'Airplane',
|
|
204
|
+
'Bookmark',
|
|
205
|
+
'RepeatCircle',
|
|
206
|
+
];
|
|
291
207
|
```
|
|
292
208
|
|
|
293
|
-
|
|
209
|
+
Without the annotation the array widens to `string[]` and every name in it stops
|
|
210
|
+
being checked — the most common way an invalid name reaches runtime.
|
|
211
|
+
|
|
212
|
+
### Keep the generic in wrappers
|
|
294
213
|
|
|
295
214
|
```tsx
|
|
296
|
-
<
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
size={80}
|
|
300
|
-
animate={{
|
|
301
|
-
mode: 'InterpolatePathProps',
|
|
302
|
-
autoStart: true,
|
|
303
|
-
loop: -1,
|
|
304
|
-
pathProps: (value, { inputRange }) => ({
|
|
305
|
-
fill: value.interpolate({
|
|
306
|
-
inputRange,
|
|
307
|
-
outputRange: ['#FF0000', '#FF69B4', '#FF1493', '#FF0000'],
|
|
308
|
-
}),
|
|
309
|
-
opacity: value.interpolate({
|
|
310
|
-
inputRange,
|
|
311
|
-
outputRange: [0.5, 1, 0.8, 0.5],
|
|
312
|
-
}),
|
|
313
|
-
}),
|
|
314
|
-
animationConfig: [
|
|
315
|
-
{ type: 'timing', duration: 500, useNativeDriver: false },
|
|
316
|
-
{ type: 'timing', duration: 500, useNativeDriver: false },
|
|
317
|
-
{ type: 'timing', duration: 500, useNativeDriver: false },
|
|
318
|
-
],
|
|
319
|
-
}}
|
|
320
|
-
/>
|
|
215
|
+
function Icon<T extends SvgIconType>(props: SvgIconProps<T>) {
|
|
216
|
+
return <SvgIcon {...props} />;
|
|
217
|
+
}
|
|
321
218
|
```
|
|
322
219
|
|
|
323
|
-
|
|
220
|
+
Writing `props: SvgIconProps` collapses the union and lets an outline-only name
|
|
221
|
+
pass on `type="filled"`.
|
|
324
222
|
|
|
325
|
-
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Animation
|
|
326
226
|
|
|
327
|
-
|
|
227
|
+
Every icon accepts an `animate` prop. Two modes, with different config shapes.
|
|
328
228
|
|
|
329
|
-
|
|
330
|
-
- Animate multiple properties simultaneously or in sequence
|
|
331
|
-
- Support for numeric and string values (colors, dash arrays, etc.)
|
|
229
|
+
Shared options, both modes:
|
|
332
230
|
|
|
333
|
-
|
|
231
|
+
| Option | Type | Default | Description |
|
|
232
|
+
| --------------- | ----------------------------------------------- | -------- | --------------------------------- |
|
|
233
|
+
| `mode` | `'AnimatedPathProps' \| 'InterpolatePathProps'` | required | Selects the shape below |
|
|
234
|
+
| `autoStart` | `boolean` | `false` | Start on mount |
|
|
235
|
+
| `loop` | `number` | `0` | `-1` for infinite |
|
|
236
|
+
| `returnToStart` | `boolean` | `false` | Reverse back to the initial state |
|
|
237
|
+
| `ref` | `AnimateSVGComponentValueRef` | – | Imperative control |
|
|
334
238
|
|
|
335
|
-
|
|
336
|
-
- `config`: Animation configuration (timing/spring)
|
|
337
|
-
- `animatedPathProps`: Array of properties to animate
|
|
338
|
-
- `isSequence`: Animate properties in sequence vs parallel (default: false)
|
|
339
|
-
- `autoStart`: Auto-start animation on mount (default: false)
|
|
340
|
-
- `returnToStart`: Return to initial state after animation (default: false)
|
|
341
|
-
- `loop`: Number of times to loop (-1 for infinite, default: 0)
|
|
239
|
+
### `AnimatedPathProps` — declarative
|
|
342
240
|
|
|
343
|
-
|
|
241
|
+
Specify what to animate and its values; interpolation is handled for you. Start
|
|
242
|
+
here.
|
|
344
243
|
|
|
345
244
|
```tsx
|
|
346
245
|
<SvgIcon
|
|
347
|
-
name="Scanning"
|
|
348
246
|
type="outline"
|
|
247
|
+
name="Scanning"
|
|
349
248
|
size={100}
|
|
350
249
|
animate={{
|
|
351
250
|
mode: 'AnimatedPathProps',
|
|
352
251
|
autoStart: true,
|
|
353
252
|
loop: -1,
|
|
354
253
|
returnToStart: true,
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
duration: 2000,
|
|
358
|
-
useNativeDriver: false,
|
|
359
|
-
},
|
|
254
|
+
isSequence: false,
|
|
255
|
+
config: { type: 'timing', duration: 2000, useNativeDriver: false },
|
|
360
256
|
animatedPathProps: [
|
|
361
|
-
{
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
to: ['green', 'blue'],
|
|
365
|
-
},
|
|
366
|
-
{
|
|
367
|
-
name: 'strokeDasharray',
|
|
368
|
-
from: '18, 18',
|
|
369
|
-
to: ['36, 36', '18, 18'],
|
|
370
|
-
},
|
|
371
|
-
{
|
|
372
|
-
name: 'strokeDashoffset',
|
|
373
|
-
from: 72,
|
|
374
|
-
to: [18, 36],
|
|
375
|
-
},
|
|
257
|
+
{ name: 'stroke', from: 'red', to: ['green', 'blue'] },
|
|
258
|
+
{ name: 'strokeDasharray', from: '18, 18', to: ['36, 36', '18, 18'] },
|
|
259
|
+
{ name: 'strokeDashoffset', from: 72, to: [18, 36] },
|
|
376
260
|
],
|
|
377
261
|
}}
|
|
378
262
|
/>
|
|
379
263
|
```
|
|
380
264
|
|
|
381
|
-
|
|
265
|
+
| Option | Type | Description |
|
|
266
|
+
| ------------------- | ----------------------- | -------------------------------------------------- |
|
|
267
|
+
| `config` | timing or spring config | Single config for the whole animation |
|
|
268
|
+
| `animatedPathProps` | `{ name, from, to }[]` | Properties to animate; `to` is an array of stages |
|
|
269
|
+
| `isSequence` | `boolean` | Run properties in sequence rather than in parallel |
|
|
270
|
+
|
|
271
|
+
### `InterpolatePathProps` — manual
|
|
272
|
+
|
|
273
|
+
Gives you the `Animated.Value` to interpolate yourself. Use when the declarative
|
|
274
|
+
form can't express what you need.
|
|
382
275
|
|
|
383
276
|
```tsx
|
|
384
277
|
<SvgIcon
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
size={
|
|
278
|
+
type="outline"
|
|
279
|
+
name="FingerScan"
|
|
280
|
+
size={100}
|
|
388
281
|
animate={{
|
|
389
|
-
mode: '
|
|
390
|
-
isSequence: true,
|
|
282
|
+
mode: 'InterpolatePathProps',
|
|
391
283
|
autoStart: true,
|
|
392
284
|
loop: -1,
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
from: '#FFD700',
|
|
403
|
-
to: ['#FFA500', '#FFD700'],
|
|
404
|
-
},
|
|
405
|
-
{
|
|
406
|
-
name: 'opacity',
|
|
407
|
-
from: 1,
|
|
408
|
-
to: [0.5, 1],
|
|
409
|
-
},
|
|
285
|
+
returnToStart: true,
|
|
286
|
+
pathProps: (value, { inputRange }) => ({
|
|
287
|
+
strokeLinejoin: 'miter',
|
|
288
|
+
stroke: value.interpolate({ inputRange, outputRange: ['red', 'green'] }),
|
|
289
|
+
strokeDashoffset: value.interpolate({ inputRange, outputRange: [36, 0] }),
|
|
290
|
+
strokeDasharray: '35, 35',
|
|
291
|
+
}),
|
|
292
|
+
animationConfig: [
|
|
293
|
+
{ type: 'timing', duration: 1000, useNativeDriver: false },
|
|
410
294
|
],
|
|
411
295
|
}}
|
|
412
296
|
/>
|
|
413
297
|
```
|
|
414
298
|
|
|
415
|
-
|
|
299
|
+
| Option | Type | Description |
|
|
300
|
+
| ----------------- | -------------------------------------- | ------------------------------------------------------------ |
|
|
301
|
+
| `animationConfig` | config or config array | One entry per stage; `inputRange` is derived from the length |
|
|
302
|
+
| `pathProps` | `(value, { inputRange }) => PathProps` | Returns path props from the animated value |
|
|
416
303
|
|
|
417
|
-
|
|
304
|
+
Note `animate.pathProps` (a function) is distinct from the top-level `pathProps`
|
|
305
|
+
prop (a static object). Both can be present; the top-level object is applied
|
|
306
|
+
first and the animated result merged over it.
|
|
418
307
|
|
|
419
|
-
|
|
308
|
+
### Config shapes
|
|
420
309
|
|
|
421
310
|
```tsx
|
|
422
|
-
{
|
|
423
|
-
|
|
424
|
-
duration: 1000,
|
|
425
|
-
delay?: 0,
|
|
426
|
-
easing?: Easing.linear,
|
|
427
|
-
useNativeDriver: true, // or false for color/transform animations
|
|
428
|
-
}
|
|
311
|
+
{ type: 'timing', duration: 1000, delay: 0, easing: Easing.linear, useNativeDriver: false }
|
|
312
|
+
{ type: 'spring', tension: 40, friction: 7, speed: 12, bounciness: 8, useNativeDriver: false }
|
|
429
313
|
```
|
|
430
314
|
|
|
431
|
-
|
|
315
|
+
### `useNativeDriver`
|
|
432
316
|
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
bounciness?: 8,
|
|
440
|
-
useNativeDriver: true, // or false for color/transform animations
|
|
441
|
-
}
|
|
442
|
-
```
|
|
317
|
+
Use `false` for `stroke`, `fill`, `opacity` and dash properties — which covers
|
|
318
|
+
most icon animation. `true` is only valid for transform-based animation. The
|
|
319
|
+
native driver cannot animate SVG path attributes, and getting this wrong fails
|
|
320
|
+
silently on one platform and throws on the other.
|
|
321
|
+
|
|
322
|
+
### Animation applies to every path
|
|
443
323
|
|
|
444
|
-
|
|
324
|
+
Multi-path outline icons animate in lockstep. There is no per-path targeting
|
|
325
|
+
through this API.
|
|
445
326
|
|
|
446
|
-
|
|
327
|
+
### Imperative control
|
|
447
328
|
|
|
448
329
|
```tsx
|
|
449
330
|
import { useRef } from 'react';
|
|
450
331
|
import type { AnimateSVGComponentValueRef } from '@shaquillehinds/react-native-essentials';
|
|
451
332
|
|
|
452
333
|
function ControlledIcon() {
|
|
453
|
-
const
|
|
454
|
-
|
|
455
|
-
const handleStart = () => animationRef.current?.start();
|
|
456
|
-
const handleStop = () => animationRef.current?.stop();
|
|
457
|
-
const handleReset = () => animationRef.current?.reset();
|
|
458
|
-
const handleReverse = () => animationRef.current?.reverse();
|
|
334
|
+
const ref = useRef<AnimateSVGComponentValueRef>(null);
|
|
459
335
|
|
|
460
336
|
return (
|
|
461
337
|
<>
|
|
462
338
|
<SvgIcon
|
|
463
|
-
name="Play"
|
|
464
339
|
type="filled"
|
|
340
|
+
name="Play"
|
|
465
341
|
size={60}
|
|
466
342
|
animate={{
|
|
467
|
-
ref
|
|
343
|
+
ref,
|
|
468
344
|
mode: 'InterpolatePathProps',
|
|
469
345
|
autoStart: false,
|
|
470
346
|
pathProps: (value) => ({
|
|
@@ -480,118 +356,45 @@ function ControlledIcon() {
|
|
|
480
356
|
},
|
|
481
357
|
}}
|
|
482
358
|
/>
|
|
483
|
-
<Button title="Start" onPress={
|
|
484
|
-
<Button title="Stop" onPress={
|
|
485
|
-
<Button title="Reset" onPress={
|
|
486
|
-
<Button title="Reverse" onPress={
|
|
359
|
+
<Button title="Start" onPress={() => ref.current?.start()} />
|
|
360
|
+
<Button title="Stop" onPress={() => ref.current?.stop()} />
|
|
361
|
+
<Button title="Reset" onPress={() => ref.current?.reset()} />
|
|
362
|
+
<Button title="Reverse" onPress={() => ref.current?.reverse()} />
|
|
487
363
|
</>
|
|
488
364
|
);
|
|
489
365
|
}
|
|
490
366
|
```
|
|
491
367
|
|
|
492
|
-
|
|
368
|
+
The ref type is exported from `@shaquillehinds/react-native-essentials`.
|
|
493
369
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
```tsx
|
|
497
|
-
<SvgIcon
|
|
498
|
-
name="Notification"
|
|
499
|
-
type="filled"
|
|
500
|
-
size={40}
|
|
501
|
-
animate={{
|
|
502
|
-
mode: 'AnimatedPathProps',
|
|
503
|
-
autoStart: true,
|
|
504
|
-
loop: -1,
|
|
505
|
-
returnToStart: true,
|
|
506
|
-
config: { type: 'timing', duration: 1000, useNativeDriver: false },
|
|
507
|
-
animatedPathProps: [{ name: 'opacity', from: 1, to: [0.3, 1] }],
|
|
508
|
-
}}
|
|
509
|
-
/>
|
|
510
|
-
```
|
|
511
|
-
|
|
512
|
-
#### Rotating Stroke Dash
|
|
370
|
+
---
|
|
513
371
|
|
|
514
|
-
|
|
515
|
-
<SvgIcon
|
|
516
|
-
name="Loading"
|
|
517
|
-
type="outline"
|
|
518
|
-
size={50}
|
|
519
|
-
animate={{
|
|
520
|
-
mode: 'AnimatedPathProps',
|
|
521
|
-
autoStart: true,
|
|
522
|
-
loop: -1,
|
|
523
|
-
config: { type: 'timing', duration: 1500, useNativeDriver: false },
|
|
524
|
-
animatedPathProps: [
|
|
525
|
-
{
|
|
526
|
-
name: 'strokeDashoffset',
|
|
527
|
-
from: 0,
|
|
528
|
-
to: [100],
|
|
529
|
-
},
|
|
530
|
-
],
|
|
531
|
-
}}
|
|
532
|
-
/>
|
|
533
|
-
```
|
|
372
|
+
## Bundle size
|
|
534
373
|
|
|
535
|
-
|
|
374
|
+
`SvgIcon` resolves names through a registry that statically imports all 1,994 icon
|
|
375
|
+
modules. Importing `SvgIcon` anywhere brings the full set into the bundle —
|
|
376
|
+
unused icons are not tree-shaken away.
|
|
536
377
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
type="outline"
|
|
541
|
-
size={80}
|
|
542
|
-
animate={{
|
|
543
|
-
mode: 'InterpolatePathProps',
|
|
544
|
-
autoStart: true,
|
|
545
|
-
loop: -1,
|
|
546
|
-
pathProps: (value) => ({
|
|
547
|
-
stroke: value.interpolate({
|
|
548
|
-
inputRange: [0, 0.33, 0.66, 1],
|
|
549
|
-
outputRange: ['#FF0000', '#00FF00', '#0000FF', '#FF0000'],
|
|
550
|
-
}),
|
|
551
|
-
}),
|
|
552
|
-
animationConfig: [
|
|
553
|
-
{ type: 'timing', duration: 3000, useNativeDriver: false },
|
|
554
|
-
],
|
|
555
|
-
}}
|
|
556
|
-
/>
|
|
557
|
-
```
|
|
378
|
+
In practice the icons are small path strings and the cost is modest, but plan for
|
|
379
|
+
it rather than assuming per-icon shaking. There are currently no per-icon entry
|
|
380
|
+
points; the package exports `SvgIcon` and the name types.
|
|
558
381
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
- Set `useNativeDriver: true` when animating transform properties for better performance
|
|
562
|
-
- Set `useNativeDriver: false` when animating colors, stroke properties, or opacity
|
|
563
|
-
- For complex animations, consider using `InterpolatePathProps` mode for better control
|
|
564
|
-
- Use `AnimatedPathProps` mode for simpler, declarative animations
|
|
382
|
+
---
|
|
565
383
|
|
|
566
|
-
##
|
|
384
|
+
## Recipes
|
|
567
385
|
|
|
568
|
-
###
|
|
386
|
+
### Icon button
|
|
569
387
|
|
|
570
388
|
```tsx
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
function IconButton({ onPress }: { onPress: () => void }) {
|
|
575
|
-
return (
|
|
576
|
-
<TouchableOpacity onPress={onPress}>
|
|
577
|
-
<SvgIcon type="filled" name="Heart" size={24} color="#FF0000" />
|
|
578
|
-
</TouchableOpacity>
|
|
579
|
-
);
|
|
580
|
-
}
|
|
389
|
+
<TouchableOpacity onPress={onPress}>
|
|
390
|
+
<SvgIcon type="filled" name="Heart" size={24} color="#FF0000" />
|
|
391
|
+
</TouchableOpacity>
|
|
581
392
|
```
|
|
582
393
|
|
|
583
|
-
### Tab
|
|
394
|
+
### Tab bar
|
|
584
395
|
|
|
585
396
|
```tsx
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
function TabBarIcon({
|
|
589
|
-
focused,
|
|
590
|
-
name,
|
|
591
|
-
}: {
|
|
592
|
-
focused: boolean;
|
|
593
|
-
name: 'Home' | 'Search' | 'Profile';
|
|
594
|
-
}) {
|
|
397
|
+
function TabBarIcon({ focused, name }: { focused: boolean; name: IconName }) {
|
|
595
398
|
return (
|
|
596
399
|
<SvgIcon
|
|
597
400
|
type={focused ? 'filled' : 'outline'}
|
|
@@ -603,84 +406,137 @@ function TabBarIcon({
|
|
|
603
406
|
}
|
|
604
407
|
```
|
|
605
408
|
|
|
606
|
-
|
|
409
|
+
Only safe for names present in both variants.
|
|
410
|
+
|
|
411
|
+
### Icon grid
|
|
607
412
|
|
|
608
413
|
```tsx
|
|
609
|
-
import {
|
|
610
|
-
import { SvgIcon } from 'react-native-svg-icons';
|
|
611
|
-
import type { FilledIconName } from 'react-native-svg-icons';
|
|
414
|
+
import type { FilledIconName } from '@shaquillehinds/react-native-svg-icons';
|
|
612
415
|
|
|
613
|
-
|
|
614
|
-
const icons: FilledIconName[] = ['Heart', 'Star', 'User', 'Setting', 'Home'];
|
|
416
|
+
const icons: FilledIconName[] = ['Heart', 'Star', 'User', 'Setting', 'Home'];
|
|
615
417
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
</View>
|
|
622
|
-
);
|
|
623
|
-
}
|
|
418
|
+
<View style={{ flexDirection: 'row', gap: 16 }}>
|
|
419
|
+
{icons.map((icon) => (
|
|
420
|
+
<SvgIcon key={icon} type="filled" name={icon} size={32} color="#000" />
|
|
421
|
+
))}
|
|
422
|
+
</View>;
|
|
624
423
|
```
|
|
625
424
|
|
|
626
|
-
###
|
|
627
|
-
|
|
628
|
-
See the [Animation](#animation) section for comprehensive examples of animating icons with the built-in `animate` prop.
|
|
425
|
+
### Pulse
|
|
629
426
|
|
|
630
|
-
|
|
427
|
+
```tsx
|
|
428
|
+
<SvgIcon
|
|
429
|
+
type="filled"
|
|
430
|
+
name="Notification"
|
|
431
|
+
size={40}
|
|
432
|
+
animate={{
|
|
433
|
+
mode: 'AnimatedPathProps',
|
|
434
|
+
autoStart: true,
|
|
435
|
+
loop: -1,
|
|
436
|
+
returnToStart: true,
|
|
437
|
+
config: { type: 'timing', duration: 1000, useNativeDriver: false },
|
|
438
|
+
animatedPathProps: [{ name: 'opacity', from: 1, to: [0.3, 1] }],
|
|
439
|
+
}}
|
|
440
|
+
/>
|
|
441
|
+
```
|
|
631
442
|
|
|
632
|
-
|
|
633
|
-
2. **Memoize Icon Components**: Use `React.memo` for frequently re-rendered icons
|
|
634
|
-
3. **Use Appropriate Sizes**: Stick to common sizes (16, 20, 24, 32, 48) for better caching
|
|
635
|
-
4. **Tree Shaking**: Only imported icons are included in your bundle
|
|
443
|
+
### Draw-on stroke
|
|
636
444
|
|
|
637
445
|
```tsx
|
|
638
|
-
|
|
639
|
-
|
|
446
|
+
<SvgIcon
|
|
447
|
+
type="outline"
|
|
448
|
+
name="TickCircle"
|
|
449
|
+
size={64}
|
|
450
|
+
animate={{
|
|
451
|
+
mode: 'AnimatedPathProps',
|
|
452
|
+
autoStart: true,
|
|
453
|
+
config: { type: 'timing', duration: 800, useNativeDriver: false },
|
|
454
|
+
animatedPathProps: [{ name: 'strokeDashoffset', from: 100, to: [0] }],
|
|
455
|
+
}}
|
|
456
|
+
/>
|
|
457
|
+
```
|
|
640
458
|
|
|
641
|
-
|
|
459
|
+
Pair with `pathProps={{ strokeDasharray: '100, 100' }}` to set the dash pattern.
|
|
642
460
|
|
|
643
|
-
|
|
644
|
-
<MemoizedIcon type="filled" name="Heart" size={24} color="#FF0000" />;
|
|
645
|
-
```
|
|
461
|
+
---
|
|
646
462
|
|
|
647
463
|
## Troubleshooting
|
|
648
464
|
|
|
649
|
-
|
|
465
|
+
**Nothing renders where an icon should be.** Almost always a bad name. An unknown
|
|
466
|
+
`type` or `name` logs through `console.error` and returns `null` — it does not
|
|
467
|
+
throw. Check the console, then verify with `npx rnsi-icons <fragment>`.
|
|
650
468
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
469
|
+
**TypeScript rejects a name that looks right.** Check the misspelling list, and
|
|
470
|
+
check whether the name exists in the variant you passed to `type`
|
|
471
|
+
(`npx rnsi-icons --diff`).
|
|
654
472
|
|
|
655
|
-
|
|
473
|
+
**Icon renders at the wrong size.** `size` is normalised for the device, so it
|
|
474
|
+
won't match physical points exactly. Also check nothing is passing `width` or
|
|
475
|
+
`height` through `svgProps`, which overrides it.
|
|
656
476
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
477
|
+
**`color` has no effect.** Something in `pathProps` is overriding it — it is
|
|
478
|
+
spread last. Also check you aren't setting `fill` on an outline icon or `stroke`
|
|
479
|
+
on a filled one.
|
|
660
480
|
|
|
661
|
-
|
|
481
|
+
**Animation doesn't run.** `autoStart` defaults to `false`. If it is set, check
|
|
482
|
+
`useNativeDriver` is `false` for colour, stroke, opacity and dash animation.
|
|
662
483
|
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
484
|
+
**Animation config is ignored.** The two modes use different keys —
|
|
485
|
+
`AnimatedPathProps` takes `config` and `animatedPathProps`, `InterpolatePathProps`
|
|
486
|
+
takes `animationConfig` and a `pathProps` function. Mixing them silently drops
|
|
487
|
+
the unrecognised half.
|
|
666
488
|
|
|
667
|
-
|
|
489
|
+
**Icons missing entirely after install.** Confirm `react-native-svg` is linked,
|
|
490
|
+
run `pod install` on iOS, and rebuild — a Metro reload is not enough for a new
|
|
491
|
+
native dependency.
|
|
668
492
|
|
|
669
|
-
|
|
493
|
+
---
|
|
670
494
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
495
|
+
## AI agent rules
|
|
496
|
+
|
|
497
|
+
The package ships a rules file written for AI coding agents (Claude Code, Cursor,
|
|
498
|
+
Codex, Copilot, etc.) at `rules/AGENT_RULES.md`. It tells an agent to verify every
|
|
499
|
+
icon name against the shipped type union instead of guessing one, spells out the
|
|
500
|
+
misspelled names and variant mismatches it would otherwise "correct", and
|
|
501
|
+
documents both animation modes so it cannot mix their config keys. Point your
|
|
502
|
+
agent at it with any of the following.
|
|
503
|
+
|
|
504
|
+
**Copy it into your project (recommended)**
|
|
505
|
+
|
|
506
|
+
```sh
|
|
507
|
+
npx rnsi-rules # writes ./AGENTS.md
|
|
508
|
+
npx rnsi-rules cursor # writes ./.cursor/rules/react-native-svg-icons.mdc (alwaysApply)
|
|
509
|
+
npx rnsi-rules claude # writes ./.claude/rules/react-native-svg-icons.md
|
|
510
|
+
npx rnsi-rules codex # writes ./.codex/rules/react-native-svg-icons.md
|
|
511
|
+
npx rnsi-rules copilot # writes ./.github/instructions/react-native-svg-icons.instructions.md
|
|
512
|
+
npx rnsi-rules windsurf # writes ./.windsurf/rules/react-native-svg-icons.md
|
|
513
|
+
npx rnsi-rules docs/ai/svg-icons.md # custom path
|
|
514
|
+
```
|
|
675
515
|
|
|
676
|
-
|
|
516
|
+
Add `--force` to overwrite an existing file. `--print` writes the rules to stdout
|
|
517
|
+
instead of to disk. Re-run after upgrading the package to pick up rule changes.
|
|
677
518
|
|
|
678
|
-
|
|
519
|
+
**Reference it without copying (Claude Code)**
|
|
520
|
+
|
|
521
|
+
`CLAUDE.md` supports `@path` imports, so a single line keeps the rules in sync with
|
|
522
|
+
the installed version:
|
|
523
|
+
|
|
524
|
+
```md
|
|
525
|
+
# CLAUDE.md
|
|
679
526
|
|
|
680
|
-
|
|
527
|
+
@node_modules/@shaquillehinds/react-native-svg-icons/rules/AGENT_RULES.md
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
**Reference it from a generic `AGENTS.md`**
|
|
681
531
|
|
|
682
|
-
|
|
532
|
+
```md
|
|
533
|
+
Before using any icon, read and follow
|
|
534
|
+
node_modules/@shaquillehinds/react-native-svg-icons/rules/AGENT_RULES.md.
|
|
535
|
+
Never guess an icon name — verify it with `npx rnsi-icons <query>`.
|
|
536
|
+
```
|
|
683
537
|
|
|
684
538
|
---
|
|
685
539
|
|
|
686
|
-
|
|
540
|
+
## License
|
|
541
|
+
|
|
542
|
+
MIT
|