Releases: marioprieta/react-native-theme-transition
v2.0.1
Patch Changes
-
7588dfa: Documentation-only release. Two fixes:
- Hero demo GIF URL. Switched from GitHub's
user-attachmentsCDN toraw.githubusercontent.com. The former requires authentication and returned 404 to npm's image proxy, so the demo was invisible on the npm package page. - Minimum Expo SDK clarified to 55+. Earlier docs listed SDK 54+, but Expo Go on SDK 54 bundles older Skia and Reanimated native modules that are incompatible with v2 at runtime (tested: freeze on Android, silent instant swap on iOS). Upgrade with
npx expo install expo@latest --fixbefore installing the library.
- Hero demo GIF URL. Switched from GitHub's
v2.0.0
Major Changes
-
390a887: v2: Skia engine, 9 transitions, radically simpler API.
The engine was rebuilt on top of
@shopify/react-native-skia. The provider
now snapshots the inner tree into a pre-mounted Skia Canvas, swaps the theme
underneath, and animates the captured frame away with one of nine transition
styles. The public API was re-shaped around a four-field hook and a
discriminated options union.The hook now splits two orthogonal concepts:
themeis always concrete
(the theme currently painted on screen), andpreferenceis the user's
raw pick (which can be'system'). No moreisSystemModeflags, no
more "picker state vs active theme" duality.Breaking: peer dependencies
react-native-view-shotis replaced by@shopify/react-native-skia(>= 2.0.0):npm uninstall react-native-view-shot npm install @shopify/react-native-skia
Minimum versions for the surrounding stack were also raised:
Peer v1 minimum v2 minimum react>=18.0.0>=19.0.0react-native>=0.76.0>=0.78.0react-native-reanimated>=4.0.0>=4.0.0react-native-worklets>=0.5.0>=0.5.0react-native-view-shot>=3.0.0removed @shopify/react-native-skianew >=2.0.0React 19 and React Native 0.78 are hard requirements: v2 reads context
via React 19'suse()hook, and@shopify/react-native-skia2.0 itself
declaresreact: ">=19.0"andreact-native: ">=0.78"as peer
dependencies. Apps on older React or React Native versions need to
upgrade before installing v2.Breaking:
useThemereturn shape// v1 const { colors, name, setTheme, isTransitioning, selected, select } = useTheme(); // v2 const { theme, preference, setTheme, isTransitioning } = useTheme(); // theme.name: Names (always concrete, never 'system') // theme.colors: Record<Tokens, string> // theme.scheme: 'light' | 'dark' // preference: Names | 'system' (the user's raw pick)
colorsandnamemoved undertheme, andtheme.schemeis a new
binary'light' | 'dark'classifier derived fromdarkThemes.
preferenceis a brand-new top-level field that mirrors the user's
raw pick (including'system') and updates synchronously inside
setThemeso pickers can drop their optimistic local state.selected,
select,initialSelection,SelectOptions,UseThemeOptions, and
useReducedMotionare gone. The engine's internalSETTLE.beforeCapture
wait gives React batching time to paint before the snapshot is taken, so
no hook magic is needed. See the Migration guide.Breaking:
setThemereturn type// v1 setTheme('dark'): boolean // v2 setTheme('dark'): 'accepted' | 'ignored'
'accepted'means the library will apply the change (instantly or via an
animated transition).'ignored'means nothing will happen (already
transitioning, or same theme in the same mode). Most callers don't need
the return value; reactive state (isTransitioning, callbacks) is
usually enough.Breaking: removed config options
reduceMotion: removed. The library no longer honors OS Reduce
Motion: these are brief color transitions, not spatial motion, and instant
swaps are arguably more jarring than a 350ms fade. Consumers who want
to honor the OS setting write their ownAccessibilityInfolistener
and passanimated: falseper call.duration: removed from config. Each transition family has its own
calibrated default (350ms fade / reveal / strip, 800ms shape, 750ms
shader). A global override would flatten that calibration. Override
per call withsetTheme(name, { duration: 400 })when you need it.backgroundColor: removed. The library no longer paints a
root background behind the inner tree. SetbackgroundColoron
your own rootViewas with any standard React Native app.
Breaking: renamed option fields
v1 v2 { transition: 'split', axis: 'horizontal' }{ transition: 'split', mode: 'top-bottom' }{ transition: 'split', axis: 'vertical' }{ transition: 'split', mode: 'left-right' }{ transition: 'dissolve', grainSize: 5 }{ transition: 'dissolve', noiseSize: 5 }{ transition: 'invertedCircularReveal' }{ transition: 'circularReveal', inverted: true }The
diamondtransition proposed in early v2 work was removed before
shipping; only the 9 below are supported.Breaking:
directionsemantics unified (wipe + slide)In v1,
direction: 'right'meant "sweep rightward" onwipebut "new
enters from the right" onslide: two opposite visual meanings for the
same string. In v2 both transitions share the same rule:direction: 'right'
is the direction the motion is heading, so the new theme enters from the
LEFT edge and sweeps rightward. Default is'right'.New transitions
On top of v1's
fade, v2 adds eight new styles:circularReveal: expanding circle from any origin (point or ref).heart: a heart-shaped clip grows from the origin.star: a 5-point star clip grows from the origin.wipe: a directional edge sweeps across the screen.slide: both old and new slide across like a carousel.split: the screen parts or shutters alongmode.pixelize: shader crossfade through a shared pixel grid.dissolve: noise-threshold disintegration.
Every reveal / shape transition accepts
inverted.SetThemeOptionsis a
discriminated union: TypeScript only accepts the extra fields valid for
the chosentransition.New:
TRANSITION_METAruntime exportThe same table that drives the type union is exported so consumers can
build transition pickers that adapt their UI without hardcoding the list:import { TRANSITION_META, TRANSITION_TYPES, } from "react-native-theme-transition"; const meta = TRANSITION_META[transition]; // meta.kind : 'fade' | 'reveal' | 'shape' | 'strip' | 'shader' // meta.needsOrigin : whether to show an origin picker // meta.invertible : whether to show an "inverted" toggle // meta.capturesNew : whether the engine needs a second snapshot (slide / pixelize) // meta.defaultDuration
Internal changes worth noting
- Cleanup timer was tightened from
setTimeout(finish, duration + 50)
tosetTimeout(finish, duration), removing v1's safety buffer that
stacked latency on fast devices. Reanimated 4's worklet completion
callback would be cleaner, but it doesn't reliably fire for the
nested-async closures the engine creates — see the comment on
finishTimerRef. SETTLE.treeRepaintis gated to reveal / shape / capturesNew transitions
so fade / wipe / split / dissolve save a frame of start latency.OverlayParamsand theDEFAULT_*constants live in
src/overlay/types.tsas a single source of truth for the engine and
the overlay.darkThemesconfig is now validated at init (unknown names throw).- Faster transitions: direct
SkImagecapture replaces the v1
view-shot to URI to RN<Image>decode pipeline, shaving roughly
30ms off every swap.
Migration
Full walkthrough at
recipes/migration.
v1.0.7
Patch Changes
-
bf50b17: Fix three engine bugs in the transition state machine:
- Clear deferred system restore when exiting system mode, preventing stale color scheme restoration
- Lift explicit Appearance override when entering system mode during an active transition
- Always return
truefromsetTheme('system')when entering system mode from a different theme
v1.0.6
v1.0.5
What's Changed
- v1.0.4: inline comments cleanup, docs mobile fix, skill update by @marioprieta in #15
- v1.0.5: restructure skill, GIF demos, OIDC release workflow by @marioprieta in #16
- Version Packages by @github-actions[bot] in #17
New Contributors
- @marioprieta made their first contribution in #15
Full Changelog: v1.0.3...v1.0.5
v1.0.3
What's Changed
- Version Packages by @github-actions[bot] in #14
Full Changelog: https://github.com/marioprieta/react-native-theme-transition/commits/v1.0.3