react-native-nitro-sfsymbols
v2.0.0
Published
Render Apple SF Symbols natively in React Native via Nitro Modules. Type-safe, accessible, tree-shakable.
Downloads
208
Maintainers
Readme
react-native-nitro-sfsymbols ✨
🍎 Render Apple SF Symbols natively in React Native via Nitro Modules. Type-safe, accessible, tree-shakable.
📚 Table of Contents
- ✨ Highlights
- 📦 Install
- 🚀 Usage
- ♿ Accessibility
- 📖 API
- ⏫ Migrating from 1.x
- ⚡ Performance notes
- 🤝 Contributing
- 💬 Support
- 🙏 Acknowledgments
- 📄 License
✨ Highlights
- 🎯 Native rendering through
UIImage(systemName:)— no bridge overhead. - 🎨 All four rendering modes: monochrome, hierarchical, palette, multicolor.
- 💫 iOS 17+ symbol effects:
bounce,pulse,scale,rotate,appear,disappear,replace,variableColor. - 🚀 Per-instance image cache keeps list scrolling allocation-free.
- 🌳 Tree-shakable: the icon catalog is opt-in via a subpath export — the core ships at ~1.5 KB.
- ♿ WCAG 2.2 AA-friendly: decorative-by-default, Dynamic Type support, Reduce Motion respected, Increase Contrast aware.
- 🛡️ Strict TypeScript 6: literal-union types,
as constconstants, full IDE autocomplete.
| Platform | Support | Minimum | |----------|---------|---------| | 🍎 iOS | full | 16.0 | | 💻 macOS / 📺 tvOS / 🥽 visionOS | full | latest | | 🤖 Android | empty placeholder (logs a single warning) | — |
📦 Install
yarn add react-native-nitro-sfsymbols react-native-nitro-modules
cd ios && pod installℹ️
react-native-nitro-modules >= 0.35is required.
🚀 Usage
import { SFSymbolView, SFSymbolWeight } from 'react-native-nitro-sfsymbols';
export function FavoriteButton({ active }: { active: boolean }) {
return (
<SFSymbolView
name={active ? 'heart.fill' : 'heart'}
size={28}
weight={SFSymbolWeight.SEMIBOLD}
tintColor={active ? '#FF3B30' : '#8E8E93'}
animation={active ? { type: 'bounce' } : undefined}
accessibilityLabel={active ? 'Remove favorite' : 'Add favorite'}
accessibilityRole="button"
/>
);
}📂 Optional curated catalog
For autocomplete on the most-used symbols, import from the /icons subpath:
import { SFIcons } from 'react-native-nitro-sfsymbols/icons';
<SFSymbolView name={SFIcons.HEART_FILL} />;The catalog is opt-in — consumers who pass arbitrary strings pay zero catalog cost in their bundle.
🎨 Hierarchical / palette colors
<SFSymbolView
name="cloud.sun.fill"
renderingMode="hierarchical"
hierarchical={{ primary: '#FFB300' }}
/>
<SFSymbolView
name="cloud.bolt.rain.fill"
renderingMode="palette"
palette={{ primary: '#2196F3', secondary: '#FFB300', tertiary: '#90CAF9' }}
/>💫 Animations (iOS 17+)
<SFSymbolView name="bell.fill" animation={{ type: 'bounce', repeating: false }} />🧘 Animations are silently skipped when Reduce Motion is enabled (WCAG 2.3.3).
♿ Accessibility
SFSymbolView is decorative by default — without an accessibilityLabel
it's hidden from VoiceOver and TalkBack (WCAG 1.1.1 Non-text Content). Pass
either of:
accessibilityLabel="Add to favorites"— explicit label.accessibilityAutoLabel— derive a label from the symbol name ("heart.fill"→"Heart").
For icon-only pressables, spread minTouchTargetStyle onto the wrapping
Pressable to meet Apple HIG / WCAG 2.5.8 (minimum 44×44 hit area):
import { Pressable } from 'react-native';
import { SFSymbolView, minTouchTargetStyle } from 'react-native-nitro-sfsymbols';
<Pressable style={minTouchTargetStyle} onPress={onTap}>
<SFSymbolView name="ellipsis" accessibilityLabel="More options" accessibilityRole="button" />
</Pressable>;size honors the user's Dynamic Type setting by default (capped at 2× via
maxFontSizeMultiplier). When the user enables Increase Contrast, an
omitted tintColor falls back to the system label color for guaranteed
contrast.
📖 API
| Prop | Type | Default |
|------|------|---------|
| name | string (or SFIcon from /icons) | required |
| fallbackName | string | undefined |
| size | number | 24 |
| weight | SFSymbolWeight | 'regular' |
| scale | SFSymbolScale | 'medium' |
| tintColor | string (hex #RRGGBB / #RGB / #RRGGBBAA) | system label |
| renderingMode | SFSymbolRenderingMode | 'monochrome' |
| hierarchical | { primary; secondary?; tertiary? } | — |
| palette | { primary; secondary?; tertiary? } | — |
| animation | { type; repeating? } | — |
| opacity | number (0–1) | 1 |
| allowFontScaling | boolean | true |
| maxFontSizeMultiplier | number | 2 |
| accessibilityAutoLabel | boolean | false |
Standard accessibilityLabel, accessibilityHint, accessibilityRole,
testID, style, etc. are forwarded.
⏫ Migrating from 1.x
- Bump
react-native-nitro-modulesto>= 0.35. - Imports:
SFIconsmoved to a subpath:- import { SFIcons } from 'react-native-nitro-sfsymbols'; + import { SFIcons } from 'react-native-nitro-sfsymbols/icons'; - Removed runtime helpers (use the constants directly):
isValidSFIcon,getAllSFIcons,camelCaseToSFSymbol,searchSFIconisValidColor,normalizeColor,clampOpacity,validateConfig,applyDefaults,optimizeProps,createHierarchicalConfig,createPaletteConfig,dimensionToSymbolSize,getPresetSize
- Constants are now plain objects (no longer
enums). Member access is identical (SFSymbolWeight.BOLD); the type is a string-literal union. - Color config keys:
primaryColor→primary,secondaryColor→secondary, etc.- hierarchical={{ primaryColor: '#FF5722' }} + hierarchical={{ primary: '#FF5722' }} SFSymbolThemewas a duplicate ofSFSymbolRenderingMode— removed. UseSFSymbolRenderingMode.tintColoris nowstring(hex). Pass'#RRGGBB','#RGB', or'#RRGGBBAA'. Numeric/processColorvalues were silently dropped before — switching to a string keeps the type honest.variableColorboolean prop removed. Useanimation={{ type: 'variableColor' }}instead, which is the actual iOS 17+ symbol effect.- The icon catalog is now curated. Some 1.x entries were removed or renamed (e.g.
TOGGLE_POWER→POWER). If TypeScript reports an unknown member, look up the symbol on Apple's SF Symbols app and pass its name as a plain string — every prop accepts arbitrary strings, so missing catalog entries are never blocking. - iOS 16+ minimum.
The full release notes live in CHANGELOG.md.
⚡ Performance notes
- 🧠 The native side caches resolved
UIImageinstances per configuration with an 8 MBNSCache. Repeated re-renders with identical props are zero-allocation. - 🔁 Property updates within a single React commit are coalesced into one render pass via the run loop.
- 🎨 The hex color parser keeps a 32-entry LRU cache.
- 📦 The catalog is shipped behind a subpath export — only consumers that
import 'react-native-nitro-sfsymbols/icons'pay for it.
🤝 Contributing
Contributions are very welcome! Please read CONTRIBUTING.md for the development workflow, and our Code of Conduct to keep the project a safe and inclusive space.
Quick start:
git clone https://github.com/mCodex/react-native-nitro-sfsymbols.git
cd react-native-nitro-sfsymbols
yarn
yarn typecheck && yarn lint && yarn testTo run the example app:
cd example && yarn ios💬 Support
- 🐛 Found a bug? Open an issue.
- 💡 Have an idea? Start a discussion.
- ⭐ Like the project? Star it on GitHub — it helps a lot!
🙏 Acknowledgments
- Margelo for Nitro Modules — the foundation that makes this library tick.
- Apple's SF Symbols team for an incredible icon system.
- Everyone who has contributed to this project. 💖
📄 License
MIT © Mateus Andrade
