惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

腾讯CDC
N
Netflix TechBlog - Medium
aimingoo的专栏
aimingoo的专栏
P
Proofpoint News Feed
F
Fortinet All Blogs
大猫的无限游戏
大猫的无限游戏
I
InfoQ
V
V2EX
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
有赞技术团队
有赞技术团队
G
Google Developers Blog
L
LangChain Blog
博客园_首页
M
MIT News - Artificial intelligence
H
Hackread – Cybersecurity News, Data Breaches, AI and More
月光博客
月光博客
IT之家
IT之家
量子位
宝玉的分享
宝玉的分享
S
SegmentFault 最新的问题
Stack Overflow Blog
Stack Overflow Blog
V
Visual Studio Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
雷峰网
雷峰网

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
How to animate theme transitions in React Native (Expo Go...
Mario Prieta · 2026-04-24 · via DEV Community

In most React Native apps, tapping a theme toggle causes the colors to flip instantly. When every detail matters and the user experience has to feel premium, that abrupt change is worth fixing.

The approach that works is to overlay a snapshot of the current view while the theme swaps underneath. Skia's makeImageFromView makes this work in Expo Go without a prebuild.

I built react-native-theme-transition to handle this pattern. The rest of this post covers installation, per-call options, integration with Expo Router, system appearance, and persistence, and closes with how the engine works.

If you use a coding agent, you can install the library's skill and the agent will handle the install, babel config, provider wrap, and sample usage.

npx skills add https://skills.sh/marioprieta/skills/react-native-theme-transition

Install

npx expo install react-native-theme-transition @shopify/react-native-skia react-native-worklets
Add react-native-worklets/plugin as the last plugin in babel.config.js. On Expo SDK 55+ you don't need react-native-reanimated/plugin, since babel-preset-expo already includes it.

Minimal setup

Define your themes in one file:

// theme.ts
import { createThemeTransition } from 'react-native-theme-transition'

export const { ThemeTransitionProvider, useTheme } = createThemeTransition({
  themes: {
    light: { background: '#ffffff', text: '#111111', primary: '#007AFF' },
    dark:  { background: '#0b1120', text: '#f9fafb', primary: '#60a5fa' },
  },
})

Enter fullscreen mode Exit fullscreen mode

Wrap your app:

// App.tsx
import { ThemeTransitionProvider } from './theme'

export default function App() {
  return (
    <ThemeTransitionProvider initialTheme="light">
      <MyApp />
    </ThemeTransitionProvider>
  )
}

Enter fullscreen mode Exit fullscreen mode

Toggle the theme with one of the nine transitions:

import { useTheme } from './theme'

function MyScreen() {
  const { theme, setTheme } = useTheme()
  const next = theme.name === 'dark' ? 'light' : 'dark'

  return (
    <View style={{ backgroundColor: theme.colors.background }}>
      <Pressable onPress={() => setTheme(next, { transition: 'circularReveal' })}>
        <Text style={{ color: theme.colors.text }}>Toggle</Text>
      </Pressable>
    </View>
  )
}

Enter fullscreen mode Exit fullscreen mode

That is the smallest working setup. Try it on Snack (iOS) to see all nine transitions live. For React Native CLI setups or edge-case babel configs, see the installation docs.

What you can tweak

Every setTheme call takes a transition-specific options object. Shared across all transitions: duration, easing, animated. Per transition:

circularReveal, heart, star accept origin (a view ref or {x, y}) and inverted
wipe and slide take a direction
split takes a mode and inverted
pixelize and dissolve take shader parameters
Example, using circularReveal from the tap target:

const ref = useRef<View>(null)

<Pressable
  ref={ref}
  onPress={() => setTheme(next, {
    transition: 'circularReveal',
    origin: ref,
    duration: 500,
    easing: Easing.out(Easing.cubic),
  })}
>
  <Text>Toggle</Text>
</Pressable>

Enter fullscreen mode Exit fullscreen mode

TypeScript narrows the options to the fields valid for the chosen transition, so you cannot pass blockSize to fade by accident. The API reference has the full list per transition.

Wiring it up

useTheme() returns:

const { theme, preference } = useTheme()
// theme.name      → your theme keys, inferred (e.g. 'light' | 'dark' | 'ocean')
// theme.colors    → Record<Tokens, string>, typed from your themes
// theme.scheme    → 'light' | 'dark' (binary classifier)
// preference      → theme name or 'system' (the user's raw pick)

Enter fullscreen mode Exit fullscreen mode

The themes object accepts any keys (e.g. light/dark, or default/sepia/night). TypeScript narrows setTheme(name, ...) to the keys you passed.

Use theme.colors for styles and theme.scheme === 'dark' wherever a binary flag is expected (React Navigation's dark prop, UI libraries like Tamagui or Gluestack).

For follow-the-OS defaults, pass systemThemeMap (mapping system light/dark to two of your theme keys) and set initialTheme="system". To persist, save preference to storage and pass it as initialTheme on next launch.

The recipes cover persistence with AsyncStorage, MMKV, and Zustand, plus React Navigation and Expo Router integrations.

How it works

Four steps run in sequence on every theme swap:

1. Capture    →  makeImageFromView() returns an SkImage of the current view
2. Overlay    →  mount the SkImage on a permanently-mounted Skia Canvas
3. Swap       →  re-render the tree underneath with the new theme tokens
4. Animate    →  fade, reveal, or shader the overlay away

Enter fullscreen mode Exit fullscreen mode

The overlay hides the color swap in step 3 from the user's eye. The animation in step 4 is what the user actually perceives as the transition. The Skia Canvas stays mounted between transitions with a null image, so there's no remount flash.

The engine is ~600 lines in transitionEngine.tsx if you want to read the full implementation.

Next steps

The snapshot-overlay-swap-animate technique works beyond themes. Any time you need to animate between two full tree states (route transitions, onboarding flows, settings panels), the same approach applies.

Repo and issues: github.com/marioprieta/react-native-theme-transition
Live demo: Snack on iOS (Android Snack preview runs slow; real Android devices are fine)
Full docs: react-native-theme-transition.vercel.app