Vue NativeVue Native
Guide
Components
Composables
Navigation
  • iOS
  • Android
  • macOS
GitHub
Guide
Components
Composables
Navigation
  • iOS
  • Android
  • macOS
GitHub
  • Getting Started

    • Introduction
    • Installation
    • Your First App
    • Project Structure
  • Core Concepts

    • Components
    • Styling
    • Navigation
    • Native Modules
    • Native Code Blocks
    • Hot Reload
  • Advanced

    • Error Handling
    • Accessibility
    • TypeScript
    • Performance
    • Shared Element Transitions
    • Testing
    • Security
    • Debugging
    • Teleport
    • Forms and v-model
  • Integration Guides

    • State Management
    • Deep Linking & Universal Links
    • State Persistence
    • Push Notifications
    • Error Reporting & Monitoring
  • Tooling

    • Managed Workflow
    • VS Code Extension
    • Neovim Plugin
  • Building & Releasing

    • Building for Release
    • Deployment & App Store Submission
  • Reference

    • Migration & Upgrade Guide
    • Known Limitations & Platform Differences
    • Troubleshooting

Shared Element Transitions

Shared element transitions animate views between screens during navigation. When an image on a list screen and the same image on a detail screen share an identifier, the framework animates position and size between them for a seamless visual transition.

Setup

Register shared elements using useSharedElementTransition in both the source and destination screens:

<!-- ListScreen.vue -->
<script setup>
import { useSharedElementTransition } from '@thelacanians/vue-native-navigation'
import { useRouter } from '@thelacanians/vue-native-navigation'

const router = useRouter()
const { register } = useSharedElementTransition('hero-image')

function onImageMounted(viewId) {
  register(viewId)
}

function goToDetail(id) {
  router.push('Detail', { id }, { sharedElements: ['hero-image'] })
}
</script>

<template>
  <VView :style="{ flex: 1 }">
    <VImage
      ref="imageRef"
      :source="{ uri: imageUrl }"
      :style="{ width: 100, height: 100, borderRadius: 8 }"
    />
    <VButton :onPress="() => goToDetail(item.id)">
      <VText>View Detail</VText>
    </VButton>
  </VView>
</template>
<!-- DetailScreen.vue -->
<script setup>
import { useSharedElementTransition } from '@thelacanians/vue-native-navigation'

const { register } = useSharedElementTransition('hero-image')
</script>

<template>
  <VView :style="{ flex: 1 }">
    <VImage
      :source="{ uri: imageUrl }"
      :style="{ width: '100%', height: 300 }"
    />
  </VView>
</template>

How It Works

  1. Registration: Each screen registers views with a shared element ID using useSharedElementTransition(id).

  2. Navigation: When calling router.push(), pass sharedElements in the options:

    router.push('Detail', { id: 42 }, {
      sharedElements: ['hero-image', 'title-text']
    })
    
  3. Transition: The framework:

    • Captures the source element's frame (position + size)
    • Navigates to the destination screen
    • Captures the destination element's frame
    • Animates a snapshot from source frame to destination frame
  4. Cleanup: Registrations are automatically cleaned up when the component unmounts.

API

useSharedElementTransition(id)

import { useSharedElementTransition } from '@thelacanians/vue-native-navigation'

const {
  id,         // string — the shared element identifier
  viewId,     // Ref<number | null> — the registered native view ID
  register,   // (nativeViewId: number) => void
  unregister, // () => void
} = useSharedElementTransition('hero-image')
PropertyTypeDescription
idstringThe shared element identifier.
viewIdRef<number | null>The native view ID, or null if not registered.
register(nativeViewId: number) => voidRegister a native view as this shared element.
unregister() => voidRemove the registration. Called automatically on unmount.

router.push() with Shared Elements

router.push(routeName, params?, {
  sharedElements?: string[]  // IDs of elements to animate
})

The sharedElements array contains the IDs of elements that should transition between screens. Both the source and destination screens must register elements with matching IDs.

Utility Functions

import {
  getSharedElementViewId,
  getRegisteredSharedElements,
  clearSharedElementRegistry,
} from '@thelacanians/vue-native-navigation'

// Get the native view ID for a shared element
const viewId = getSharedElementViewId('hero-image')

// Get all registered shared element IDs
const ids = getRegisteredSharedElements()

// Clear all registrations (used internally during transitions)
clearSharedElementRegistry()

Multiple Shared Elements

You can animate multiple elements simultaneously:

router.push('Detail', { id: 42 }, {
  sharedElements: ['hero-image', 'title-text', 'subtitle']
})

Each element animates independently between its source and destination frames.

Platform Support

PlatformSupport
iOSJS-driven animation using UIView.animate for position and scale interpolation.
AndroidJS-driven animation using ObjectAnimator for position and scale interpolation.

Notes

  • Shared element IDs must be unique across the currently visible screens.
  • The register() function should be called after the view is mounted and its native view ID is available.
  • Registrations are automatically cleaned up when the component is unmounted via onUnmounted.
  • The transition animation is JS-driven: the framework measures source/destination frames via the bridge and drives native animations.
Edit this page
Last Updated: 2/28/26, 11:24 PM
Contributors: Abdul Hamid, Claude Opus 4.6
Prev
Performance
Next
Testing