Skip to content

feat: Support remaining SF Symbols - #582

Open
marius-ck wants to merge 2 commits into
callstack:mainfrom
marius-ck:feat/support-remaining-sf-symbols
Open

marius-ck wants to merge 2 commits into
callstack:mainfrom
marius-ck:feat/support-remaining-sf-symbols

Conversation

@marius-ck

Copy link
Copy Markdown

https://app.notion.com/p/callstack/Support-remaining-SF-Symbols-features-from-React-Navigation-3d65d027c0f880959beafda5c40e7643?v=3285d027c0f880a68ccb000c1ce3c56e&source=copy_link

PR Description

Feature. Adds full SF Symbol configuration to tab icons, closing the gap with React Navigation 8.

Until now AppleIcon was literally { sfSymbol: SFSymbol } and the native side called UIImage(systemName:) with no configuration, so the symbol name was the only thing you could express. Alongside sfSymbol, an icon object now accepts size, color, weight, scale, variableValue, variableValueMode, renderingMode, colors and colorRenderingMode. Names, allowed values and defaults match React Navigation's SF Symbols options, so an icon configured for one behaves the same here. focusedIcon and unfocusedIcon each carry their own options.

focusedIcon: {
  sfSymbol: 'square.grid.3x2.fill',
  renderingMode: 'palette',
  colors: { primary: '#FF3B30', secondary: '#34C759' },
  weight: 'semibold',
}

The practical gain is that colored tab icons no longer require shipping a PNG or SVG. Previously every symbol was a flat monochrome template painted with the tab bar tint, so the only way to get color was an image asset, which costs the system symbol look, automatic weight and scale matching against the label font, and the automatic light/dark behavior.

Deliberately not supported. React Navigation's effect and contentTransition animate a symbol as it changes. A tab bar item renders a still image and the system never runs symbol animations on one, so both are omitted rather than accepted and silently dropped. style is absent for the same reason: the tab bar lays the item out. The new guide says so explicitly.

Two behaviors worth reviewing, both encoded in preservesOwnColors:

  • A symbol given colors of its own opts out of tabBarActiveTintColor / tabBarInactiveTintColor, since a tab item cannot hold a tint the way a live view can. This is visible in the Contacts screenshot below: the label turns blue on selection while the icon stays purple.
  • Choosing hierarchical or palette with no color at all stays tintable, so it keeps following the tab bar colors instead of falling back to black.

How it flows. TabView.tsx flattens the icon into scalar fields using sentinels for unset values, because a codegen struct cannot be null and a plain zero would otherwise read as configuration. The Fabric component view drops those sentinels into a dictionary, and the new SFSymbolOptions.swift turns it into a configured symbol image for both the SwiftUI (TabItem) and UIKit (TabViewImpl) rendering paths.

The second commit (fix: template image color) is the part that only surfaced by running the app. The options reached native intact but the colors were still being flattened to the tab tint: a tab bar templates whatever image it is handed, and SwiftUI separately applies its own symbolRenderingMode to anything it recognises as a symbol. A colored symbol now rasterizes into a plain bitmap marked .alwaysOriginal, so neither recoloring applies. This mirrors how the library already handles iconRenderingMode: 'original' for image icons.

Fully backwards compatible: with no options set, symbols render exactly as before. Android is untouched, and the existing warning about SF Symbols there still applies. A changeset is included (minor for both packages).

Docs: new SF Symbols guide documenting every option, linked from the React Navigation, Expo Router and standalone guides.

How to test?

cd apps/example/ios && pod install
cd .. && yarn start --port 8082
yarn ios --simulator "iPhone 16" --port 8082 --scheme ReactNativeBottomTabs

Open the SF Symbols example, which now exercises the new options:

  • Articleweight: 'bold' with document.fill when focused, weight: 'light' with document when not, so the icon visibly thickens and fills on selection.
  • AlbumsrenderingMode: 'palette', keeping its own color instead of the tab tint.
  • ContactsrenderingMode: 'hierarchical' with a purple base color, which stays purple while selected.
  • Signalwifi at variableValue: 0.6 and scale: 'large', showing a dimmed outer arc.

Worth also checking the Tint Colors example, since unconfigured symbols must still follow the active and inactive tints.

yarn typecheck && yarn lint && yarn test
cd docs && yarn build

Screenshots

tab-article-bar tab-contacts-bar

@marius-ck
marius-ck marked this pull request as ready for review September 17, 2026 11:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant