reactnatively

DynamicIsland

An animated Dynamic Island component for iPhone 14 Pro and later. Smoothly transitions between compact, expanded, and minimal states to surface Live Activity content at the top of the screen.

DynamicIsland renders only on iOS 16+ devices with a Dynamic Island cutout. On unsupported devices the component renders null. Use DynamicIsland.isSupported to gate rendering where needed.

Import

typescript
import { DynamicIsland } from 'reactnatively';

Now playing indicator

NowPlayingIsland.tsx
1import { useState } from 'react';2import { View, Image, StyleSheet } from 'react-native';3import { Ionicons } from '@expo/vector-icons';4import { DynamicIsland, Text, IconButton } from 'reactnatively';56export function NowPlayingIsland({ track, isPlaying, onPlayPause }) {7  const [state, setState] = useState<'compact' | 'expanded'>('compact');89  return (10    <DynamicIsland11      state={state}12      onPress={() => setState(state === 'compact' ? 'expanded' : 'compact')}13      compactContent={14        <View style={styles.compact}>15          <Ionicons name="musical-note" size={14} color="#fff" />16          <Text variant="xs" weight="semibold" numberOfLines={1} style={styles.compactTitle}>17            {track.title}18          </Text>19          <Ionicons20            name={isPlaying ? 'pause' : 'play'}21            size={14}22            color="#fff"23          />24        </View>25      }26      expandedContent={27        <View style={styles.expanded}>28          <Image source={{ uri: track.albumArt }} style={styles.art} />29          <View style={styles.info}>30            <Text variant="sm" weight="bold">{track.title}</Text>31            <Text variant="xs" color="rgba(255,255,255,0.5)">{track.artist}</Text>32          </View>33          <View style={styles.controls}>34            <IconButton35              icon={<Ionicons name="play-skip-back" size={18} color="#fff" />}36              accessibilityLabel="Previous"37              variant="ghost"38              size="sm"39            />40            <IconButton41              icon={<Ionicons name={isPlaying ? 'pause' : 'play'} size={20} color="#fff" />}42              accessibilityLabel={isPlaying ? 'Pause' : 'Play'}43              variant="ghost"44              size="md"45              onPress={onPlayPause}46            />47            <IconButton48              icon={<Ionicons name="play-skip-forward" size={18} color="#fff" />}49              accessibilityLabel="Next"50              variant="ghost"51              size="sm"52            />53          </View>54        </View>55      }56    />57  );58}5960const styles = StyleSheet.create({61  compact: { flexDirection: 'row', alignItems: 'center', gap: 6, paddingHorizontal: 8 },62  compactTitle: { maxWidth: 100 },63  expanded: { flexDirection: 'row', alignItems: 'center', gap: 12, padding: 12 },64  art: { width: 48, height: 48, borderRadius: 8 },65  info: { flex: 1 },66  controls: { flexDirection: 'row', alignItems: 'center', gap: 4 },67});

Incoming call

CallIsland.tsx
1import { View, StyleSheet } from 'react-native';2import { Ionicons } from '@expo/vector-icons';3import { DynamicIsland, Avatar, Text, IconButton } from 'reactnatively';45export function CallIsland({ caller, onAccept, onDecline }) {6  return (7    <DynamicIsland8      defaultState="expanded"9      expandedContent={10        <View style={styles.row}>11          <Avatar src={{ uri: caller.avatar }} size="sm" online="online" />12          <View style={styles.info}>13            <Text variant="xs" color="rgba(255,255,255,0.5)">Incoming call</Text>14            <Text variant="sm" weight="semibold">{caller.name}</Text>15          </View>16          <View style={styles.actions}>17            <IconButton18              icon={<Ionicons name="call" size={18} color="#fff" />}19              accessibilityLabel="Accept call"20              variant="solid"21              color="success"22              size="sm"23              borderRadius={20}24              onPress={onAccept}25            />26            <IconButton27              icon={<Ionicons name="call" size={18} color="#fff" />}28              accessibilityLabel="Decline call"29              variant="solid"30              color="error"31              size="sm"32              borderRadius={20}33              onPress={onDecline}34            />35          </View>36        </View>37      }38    />39  );40}4142const styles = StyleSheet.create({43  row: { flexDirection: 'row', alignItems: 'center', gap: 10, padding: 10 },44  info: { flex: 1 },45  actions: { flexDirection: 'row', gap: 8 },46});

Props

PropTypeDefaultDescription
state'compact' | 'expanded' | 'minimal'undefinedControlled display state.
defaultState'compact' | 'expanded' | 'minimal'"compact"Initial state for uncontrolled usage.
onStateChange(state) => voidundefinedCalled when state transitions.
compactContentReactNodeundefinedContent rendered in compact state (pill shape).
expandedContentReactNodeundefinedContent rendered in expanded state (large island).
minimalContentReactNodeundefinedContent rendered in minimal state (dot).
onPress() => voidundefinedCalled when the island is tapped.
styleStyleProp<ViewStyle>undefinedStyle applied to the outer container.