Skip to content
Native Animation
Empty State Scene animation preview

This preview uses fictional demo content. See the props below for the customization options available in this component.

npx nativeanimation add empty-state-scene
Skia

Empty State Scene

An empty-library scene that lights up and reveals a note, with configurable copy, fonts, accent colors, and ambient intensity.

$9 for this animation

Add to your project

Copy the component and its assets into your app with the Native Animation CLI, or paste the agent prompt into Claude Code, Codex or Cursor once the MCP server is connected.

About this animation

An empty-library scene that lights up and reveals a note, with configurable copy, fonts, accent colors, and ambient intensity.

Why this one

The illustration participates in the action: a moth finds the light, the book opens, and the lamp’s warm light becomes the first note. One persistent surface morphs its position, size, corner radius, and gradient into the note background, with no flying-card handoff. A circular daylight reveal begins at the lamp. Reset traces the same geometry in reverse, and the lamp can also be switched independently without creating a note.

How it works

  1. onLayout measures the containing view. A vector Skia scene fills 80% of its width, with a height limit to keep controls separate on smaller phones.
  2. A UI-thread clock drives a four-second, one-degree lamp sway and the moth's Lissajous flight.
  3. A single interruptible spring drives note progress. Its default settings are damping: 28, stiffness: 240, and mass: 1 (damping ratio ≈ 0.90), with overshoot clamped. The duration knob scales stiffness and damping together to preserve that ratio. CTA feedback goes from 0.97 to 1 without overshoot.
  4. The same Skia rounded surface starts as a warm radial light field at the shade and becomes the measured note-row background. Progress 0–0.2 grows the light; 0.2–0.85 morphs its bounds, radius, and gradient. A circle reveals daylight from the lamp without fading entire screens.
  5. The scene and empty-state copy fade out over progress 0.18–0.48 while moving exactly 12 points down. Note text and controls fade in only at 0.85–1, after the scene has left and the material has reached its destination. The surface stays mounted throughout.
  6. Reset springs that same progress back to zero, reversing all geometry and visibility intervals. Reduced motion keeps the note surface at its destination and uses sequential local fades, with no travel, ambient motion, or expanding reveal. Separate illumination exists only for the lamp-only interaction.

Install

npx expo install @shopify/react-native-skia expo-linear-gradient react-native-reanimated react-native-worklets

Copy this folder without demo/. For web, load CanvasKit before mounting the component using your application's Skia web initialization boundary. There are no remote assets or additional font dependencies; font families are supplied by the host.

Usage

import EmptyStateScene from './empty-state-scene';

export function Library() {
  return <EmptyStateScene onCreateNote={() => createDraft()} />;
}
import { useRef } from 'react';
import { EmptyStateScene, type EmptyStateSceneRef } from './empty-state-scene';

export function CustomLibrary() {
  const scene = useRef<EmptyStateSceneRef>(null);
  return (
    <EmptyStateScene
      ref={scene}
      title="Make room for an idea"
      ctaLabel="Create a note"
      accentColors={['#FBBF24', '#F97316']}
      ambientIntensity={0.6}
      transitionDuration={1100}
      onImport={() => openFilePicker()}
      onNotePress={() => openDraft()}
    />
  );
}

The import action triggers the same illustrated first-content transition. onImport delegates actual file selection to the host; this component does not read files or persist notes. Bottom tabs provide product context and are decorative. The search field is a disabled visual placeholder.

Props

PropTypeDefaultPurpose
headingstringLibraryScreen heading.
titlestringNothing here yetEmpty headline.
bodystringYour notes, highlights and books will glow here.Supporting copy.
ctaLabelstringWrite your first notePrimary action and accessible label.
importLabelstringImport from filesSecondary action and accessible label.
noteTitlestringUntitled noteFirst content title.
noteMetadatastringJust nowRow metadata.
headerAdornmentReactNode—Optional host brand tile and wordmark.
fontFamilystringSystemUI font.
titleFontFamilystringSystemEditorial font.
nightColorstring#14110DNight background.
dayColorstring#FBF7EEDay background.
accentColorsreadonly [string, string]['#FBBF24', '#F97316']Action gradient.
ambientIntensitynumber1Lamp sway and moth orbit amplitude.
transitionDurationnumber1100Response timescale in ms; scales the spring while preserving damping ratio.
onCreateNote() => void—New draft request.
onImport() => void—File selection request.
onNotePress() => void—First row selection.
styleStyleProp<ViewStyle>—Container override.
testIDstring—Root identifier.

Ref methods: createNote(), reset(), and toggleLamp(). Autoplay is isolated in the demo: create at 1.1 seconds, hold the settled note for three seconds, reset at 5.2 seconds, repeat every 6.6 seconds.

Compatibility

Expo SDK 58, React Native 0.88.0, Reanimated 4.7.1, and Skia 2.14.0. Uses the installed Expo Go modules; no additional native module is introduced. Supports iOS, Android, and web.

Accessibility

Buttons expose roles and labels; the lamp exposes its checked state as a switch. Tap targets are at least 44 points. Hidden empty/content controls leave the accessibility tree. Reduced motion disables the moth, lamp sway, cover rotation, surface travel, and expanding reveal, using sequential local fades instead. No gesture is required. Pass localized action copy through props. The demo uses American English and reserves the upper-right viewer close area.

Icons: Lucide (ISC). The demo's Lumen tile uses the catalog kit's lamp glyph and amber-to-orange gradient.