About this animation
A React Native horizon glow animation gives a dark welcome screen a slowly emerging planetary rim. This component uses a full-screen Skia runtime shader to draw a narrow inner band, a soft outer halo, a warm angular highlight, and film grain. Reanimated controls the reveal and advances the highlight along the arc without per-frame React renders, while the planet interior blends back into the background. Use it for a dark onboarding entry, an account welcome, a space-themed introduction, or a focused first-run screen. Supply optional identity artwork and replace heading, supporting copy, actions, and legal text. Configure the three rim colors, background, sweep duration, grain amount, and fonts, then connect action callbacks or replay the reveal through its ref.
When to use it
- Create a dark onboarding entry.
- Introduce an account welcome screen.
- Set a space-themed visual mood.
- Replay a gradual horizon reveal.
A dark welcome screen where a blue planetary rim gradually rises into a warm, slow-moving dawn.
How it works
- The component measures its own container and positions every layer as a fraction of that width and height.
- A full-screen Skia runtime shader measures the signed distance from a large circle to build the narrow inner rim, soft outer halo, and warm angular highlight.
- One
revealshared value controls the rim width and brightness. A UI-thread clock moves the warm highlight along the arc and refreshes the film grain without React renders. - The planet interior is faded back into the background, leaving only the horizon visible.
- The primary and secondary actions scale on press and spring back without moving the static welcome copy.
Install
npx expo install @shopify/react-native-skia react-native-reanimated react-native-workletsFor web, follow Skia's CanvasKit setup and load it before rendering your app:
import { LoadSkiaWeb } from '@shopify/react-native-skia/lib/module/web';
LoadSkiaWeb({ locateFile: (file) => `/${file}` }).then(() => {
// Register or render the app here.
});Copy the CanvasKit WASM file from node_modules/canvaskit-wasm/bin/full/canvaskit.wasm into your web public directory, or return its hosted URL from locateFile.
Usage
import HorizonGlowWelcome from './HorizonGlowWelcome';
export function WelcomeScreen() {
return <HorizonGlowWelcome onPrimaryPress={() => openOnboarding()} />;
}Use the visual controls and imperative replay method to adapt the pattern:
import { useRef } from 'react';
import HorizonGlowWelcome, {
type HorizonGlowWelcomeRef,
} from './HorizonGlowWelcome';
export function WelcomeScreen() {
const welcomeRef = useRef<HorizonGlowWelcomeRef>(null);
return (
<HorizonGlowWelcome
ref={welcomeRef}
title="Welcome aboard"
subtitle="A calmer way to organize your day."
rimColors={['#2348E8', '#A8BCFF', '#FFD8AD']}
sweepSeconds={16}
grain={0.35}
onRevealEnd={() => announceReady()}
/>
);
}The forwarded ref exposes replay().
Props
| Prop | Type | Default | Description |
|---|---|---|---|
logo | React.ReactNode | Hidden | Optional logo above the title. |
title | string | 'Welcome' | Main heading. |
subtitle | string | 'Everything you need, right where you need it.' | Supporting copy. |
primaryLabel | string | 'Get started' | Primary action label. |
onPrimaryPress | () => void | — | Called when the primary action is pressed. |
secondaryPrompt | string | 'Already have an account?' | Copy before the secondary action. |
secondaryLabel | string | 'Log in' | Secondary action label. |
onSecondaryPress | () => void | — | Called when the secondary action is pressed. |
legalText | string | 'By continuing, you agree to the Terms of Service and Privacy Policy.' | Legal copy at the bottom. |
rimColors | [string, string, string] | ['#1C3FE0', '#9DB6FF', '#F2D9B8'] | Inner band, outer glow, and warm highlight colors. |
backgroundColor | string | '#18171B' | Screen background. |
sweepSeconds | number | 14 | Seconds for the warm highlight to sweep out and back. |
grain | number | 0.5 | Film grain amount from 0 to 1. |
fontFamily | string | System font | Font for supporting copy. |
titleFontFamily | string | System font | Font for headings and actions. |
autoStart | boolean | true | Starts the reveal on mount. |
onRevealEnd | () => void | — | Called when the warm highlight finishes appearing. |
style | StyleProp<ViewStyle> | — | Outer-container style. |
testID | string | — | Test identifier for the outer container. |
Compatibility
- Expo SDK 57
- React Native 0.86
- Reanimated 4.5
- React Native Worklets 0.10
- React Native Skia 2.6
- iOS: supported, not yet verified on a device
- Android: supported, not yet verified on a device
- Web: supported, pending catalog verification
- Works in Expo Go because the installed Expo SDK includes Skia, Reanimated, and Worklets
Accessibility
Both actions expose the button role and use their visible labels for screen readers. With Reduce Motion enabled, the horizon fades directly to its final state over 300 ms, the warm highlight remains still, and the grain becomes static.
FAQ
- Can I customize the glowing horizon colors and timing?
- Yes. rimColors controls the inner band, outer glow, and warm highlight. Set backgroundColor, sweepSeconds, and grain to adjust the scene.
- Does the horizon welcome work in Expo Go and on web?
- Skia, Reanimated, and Worklets are included in Expo SDK 57. On web, provide the CanvasKit WASM asset and load Skia before rendering, following the README.
- What happens with Reduce Motion enabled?
- The horizon fades to its final state over 300 ms. The warm highlight stays still and the grain becomes static; the labeled welcome actions remain available.




