Expo UI and Router for iOS
Modifiers

Modifier types

Constants, hooks and types that the SwiftUI modifiers use.

Constants

Animation

Built-in animation presets for the animation modifier. Presets:

Example

import { Host, VStack } from '@expo/ui/swift-ui';
import { animation, Animation } from '@expo/ui/swift-ui/modifiers';

function Example() {
  const [isExpanded, setIsExpanded] = useState(false);

  return (
    <Host style={{ flex: 1 }}>
      <VStack modifiers={[animation(Animation.spring({ duration: 0.8 }), isExpanded)]}>
        //...
      </VStack>
    </Host>
  );
}

shapes

Shape builders for modifiers that accept shapes, such as background and containerShape.

Shapes: roundedRectangle, capsule, rectangle, ellipse, circle, containerRelativeShape.

Example

import { background, shapes } from '@expo/ui/swift-ui/modifiers';
import { Text, Host } from '@expo/ui/swift-ui';

function Example() {
  return (
    <Host>
    <Text
      modifiers={[
        background('#000', shapes.roundedRectangle({ cornerRadius: 12 })),
      ]}
    >
      Hello, world!
    </Text>
  </Host>
  );
}

Hooks

useScrollGeometryChange(callback)

iOS 18+
ParameterType
callback(optional)(geometry: ScrollGeometry) => void

Fires when the scroll geometry changes — i.e., on every scroll update and on container/content size changes. Use to drive continuous progress UI such as page indicators, parallax, or fractional offsets.

If the callback is marked with the 'worklet' directive, it runs synchronously on the UI thread (no JS-thread round-trip); otherwise it is delivered asynchronously as a regular JS event. Both paths share the same native modifier — the worklet variant is automatically wrapped in a WorkletCallback shared object whose lifetime is managed by the hook.

This is a hook because the worklet path requires a stable shared-object reference across renders. Call it at the top of your component, then include the returned modifier in your modifiers array.

Apply to a SwiftUI ScrollView (and other scrollable views). On iOS below 18.0 the modifier is a no-op.

Returns: ModifierConfig | null

See: Official SwiftUI documentation.

Example

const geometryModifier = useScrollGeometryChange((g) => {
  'worklet';
  progress.value = g.contentOffsetX / g.containerWidth;
});

<ScrollView modifiers={[geometryModifier]} />

Event subscriptions

createModifierWithEventListener(type, eventListener, params)

ParameterType
typestring
eventListener(args: any) => void
params(optional)Record<string, any>

Creates a modifier with an event listener.

Returns: ModifierConfig

createViewModifierEventListener(modifiers)

ParameterTypeDescription
modifiersModifierConfig[]An array of modifier configs to extract event listeners from.

Create an event listener for a view modifier.

Returns: GlobalEvent

Interfaces

ModifierConfig

Base interface for all view modifiers. All modifiers must have a type field and can include arbitrary parameters.

PropertyTypeDescription
$typestring-
eventListener(optional)(args: any) => void-

Types

AccessibilityTrait

Literal type: string

The set of accessibility traits that can be added to or removed from a view with accessibilityAddTraits and accessibilityRemoveTraits.

See: Official SwiftUI documentation.

Acceptable values are: 'isButton' | 'isHeader' | 'isImage' | 'isSelected' | 'isLink' | 'isModal' | 'isSummaryElement' | 'updatesFrequently' | 'startsMediaSession' | 'allowsDirectInteraction' | 'causesPageTurn' | 'isToggle' | 'playsSound' | 'isStaticText' | 'isSearchField' | 'isKeyboardKey' | 'isTabBar'

ChainableAnimationType

PropertyTypeDescription
[VALUE_SYMBOL]() => AnimationObject-
delay(delay: number) => ChainableAnimationTypeAdds a delay before the animation starts (in seconds).
repeat(params: { autoreverses: boolean, repeatCount: number }) => ChainableAnimationTypeRepeats the animation the given number of times.

Color

Literal type: union

Acceptable values are: string | ColorValue | NamedColor

ContainerBackgroundPlacement

Literal type: string

Acceptable values are: 'widget' | 'navigation' | 'navigationSplitView'

DatePickerStyleType

Literal type: string

Acceptable values are: 'automatic' | 'compact' | 'graphical' | 'wheel'

DiscreteSymbolEffectValue

Literal type: union

Equatable primitive accepted as a discrete effect trigger.

Acceptable values are: number | string | boolean

DynamicTypeSizeValue

Literal type: string

A standard size for Dynamic Type, from xSmall through the five accessibility sizes. Mirrors SwiftUI's DynamicTypeSize.

Acceptable values are: 'xSmall' | 'small' | 'medium' | 'large' | 'xLarge' | 'xxLarge' | 'xxxLarge' | 'accessibility1' | 'accessibility2' | 'accessibility3' | 'accessibility4' | 'accessibility5'

EnvironmentConfig

Type: object shaped as below:

PropertyTypeDescription
key'editMode'-
value'active' | 'inactive' | 'transient'-

Or object shaped as below:

PropertyTypeDescription
key'colorScheme'-
value'light' | 'dark'-

Or object shaped as below:

PropertyTypeDescription
key'locale'-
valuestring-

Or object shaped as below:

PropertyTypeDescription
key'timeZone'-
valuestring-

GaugeStyleType

Literal type: string

Acceptable values are: 'automatic' | 'circular' | 'circularCapacity' | 'linear' | 'linearCapacity'

GlobalEvent

PropertyTypeDescription
onGlobalEvent(event: { nativeEvent: GlobalEventPayload }) => void-

GlobalEventPayload

PropertyTypeDescription
eventName(index signature)Record<string, any>-

IndexViewStyleConfig

Configuration for the indexViewStyle modifier.

PropertyTypeDescription
backgroundDisplayMode(optional)PageIndexBackgroundDisplayModeTranslucent background behind the page indicator dots. Useful when the dots sit on top of dark or busy content. Default: 'automatic'

InterpolatingSpringAnimationParams

PropertyTypeDescription
bounce(optional)numberExtra bounce to apply to the spring animation.
damping(optional)numberThe damping applied to the spring.
duration(optional)numberTotal animation duration (in seconds).
initialVelocity(optional)numberThe initial velocity of the animation.
mass(optional)numberThe mass attached to the spring.
stiffness(optional)numberThe stiffness of the spring.

ListStyle

Literal type: string

Acceptable values are: 'automatic' | 'plain' | 'inset' | 'insetGrouped' | 'grouped' | 'sidebar'

Literal type: string

Acceptable values are: 'automatic' | 'fixed' | 'priority'

ObservableState

Observable state shared between JavaScript and native views (Jetpack Compose on Android and SwiftUI on iOS).

Type: SharedObject extended by:

PropertyTypeDescription
onChange[listener] | nullA single listener invoked on the native UI runtime whenever the value changes (after iOS didSet and Android's setter). Assigning replaces the previous listener; assign null to clear. The initial value does not fire onChange. The callback must be a worklet so it can run synchronously on the UI thread. Attach it inside useEffect and clear it in the cleanup so the listener lifecycle matches the component lifecycle. Example
const state = useNativeState(0);

useEffect(() => {
  state.onChange = (value) => {
    'worklet';
    console.log('changed to', value);
  };
}, []);

| | value | T | The current value. Writes from a UI worklet are synchronous and immediately readable. Writes from the JS thread are scheduled to the UI thread asynchronously, the new value is not readable until the update has been applied. Prefer writing from a worklet when you need synchronous updates | | get | () => T | Reads the current value. A React Compiler compliant alternative to reading .value | | set | (value: T) => void | Writes a new value. A React Compiler-compliant alternative to assigning .value |

PageIndexBackgroundDisplayMode

Literal type: string

Acceptable values are: 'automatic' | 'always' | 'never' | 'interactive'

PageIndexDisplayMode

Literal type: string

Acceptable values are: 'automatic' | 'always' | 'never'

PickerStyleType

Literal type: string

Acceptable values are: 'automatic' | 'inline' | 'menu' | 'navigationLink' | 'palette' | 'segmented' | 'wheel'

PresentationBackgroundInteractionType

Presentation background interaction type.

Type: 'automatic' or 'enabled' or 'disabled' or object shaped as below:

PropertyTypeDescription
detentPresentationDetent-
type'enabledUpThrough'-

PresentationDetent

Presentation detent type for controlling sheet heights.

  • 'medium': System medium height (approximately half screen)
  • 'large': System large height (full screen)
  • { fraction: number }: Fraction of screen height (0-1, for example, 0.4 equals 40% of screen)
  • { height: number }: Fixed height in points

Type: 'medium' or 'large' or object shaped as below:

PropertyTypeDescription
fractionnumber-

Or object shaped as below:

PropertyTypeDescription
heightnumber-

PresentationSizingType

Literal type: string

Sizing behavior for a sheet presentation.

  • 'automatic': The system default sizing.
  • 'fitted': Sizes the sheet to fit its content.
  • 'form': A compact, centered form sheet.
  • 'page': A larger page sheet.

Acceptable values are: 'automatic' | 'fitted' | 'form' | 'page'

ProgressViewStyleType

Literal type: string

Acceptable values are: 'automatic' | 'linear' | 'circular'

Shape

Literal type: ReturnType

Acceptable values are: ReturnType<shapes.roundedRectangle> | ReturnType<shapes.capsule> | ReturnType<shapes.rectangle> | ReturnType<shapes.ellipse> | ReturnType<shapes.circle> | ReturnType<shapes.containerRelativeShape>

SpringAnimationParams

PropertyTypeDescription
blendDuration(optional)numberThe duration over which to blend between animations (in seconds).
bounce(optional)numberExtra bounce to apply to the spring animation.
dampingFraction(optional)numberThe amount of damping applied to the spring's motion.
duration(optional)numberTotal animation duration (in seconds).
response(optional)numberThe spring's response time (in seconds).

StrokeStyle

The characteristics of a stroke that traces a path.

See: Official SwiftUI documentation.

PropertyTypeDescription
dash(optional)number[]The lengths of alternating painted and unpainted segments. An empty array draws a solid line. Default: []
dashPhase(optional)numberHow far into the dash pattern the line starts. Default: 0
lineCap(optional)'butt' | 'round' | 'square'The endpoint style of a line segment. Default: 'butt'
lineJoin(optional)'miter' | 'round' | 'bevel'The join type where line segments meet. Default: 'miter'
lineWidth(optional)numberThe width of the stroked line. Default: 1
miterLimit(optional)numberThe limit past which a miter join is replaced by a bevel. Default: 10

SymbolEffect

Literal type: union

Acceptable values are: AppearSymbolEffect | BounceSymbolEffect | BreatheSymbolEffect | DisappearSymbolEffect | DrawOffSymbolEffect | DrawOnSymbolEffect | PulseSymbolEffect | RotateSymbolEffect | ScaleSymbolEffect | VariableColorSymbolEffect | WiggleSymbolEffect

SymbolEffectOptions

Animation options for a symbol effect.

See: Official Apple documentation.

PropertyTypeDescription
repeat(optional)'continuous' | 'nonRepeating' | { count: number, delay: number }How the effect repeats. Omit for the effect's natural cadence
  • 'nonRepeating' — play exactly once.
  • 'continuous' — smooth, indefinite repetition (iOS 18+).
  • { count?, delay? } — periodic repetition with optional count and delay in seconds (iOS 18+).

| | speed(optional) | number | Animation speed multiplier (1.0 = default). |

TabViewStyleConfig

Configuration for the tabViewStyle modifier.

  • 'page' — swipeable horizontal pager with optional dot indicators.
  • 'automatic' — SwiftUI's default tab-bar style.
  • 'sidebarAdaptable' — iOS 18+. Sidebar on iPad/Mac, bottom bar on iPhone.

Type: object shaped as below:

PropertyTypeDescription
indexDisplayMode(optional)PageIndexDisplayModeVisibility of the page indicator dots. Only meaningful for the page style. Default: 'automatic'
type'page'-

Or object shaped as below:

PropertyTypeDescription
type'automatic'-

Or object shaped as below:

PropertyTypeDescription
type'sidebarAdaptable'-

TimingAnimationParams

PropertyTypeDescription
duration(optional)numberTotal animation duration (in seconds).

UnitPointValue

Literal type: string

Acceptable values are: 'zero' | 'topLeading' | 'top' | 'topTrailing' | 'leading' | 'center' | 'trailing' | 'bottomLeading' | 'bottom' | 'bottomTrailing'

Sources

The original Expo docs for this page:

  • Modifiers (source-of-truth/versions/v57.0.0/sdk/ui/swift-ui/modifiers.md)

On this page