Skip to content

@flyva/nuxt

Nuxt 4 module. Components and composables are auto-imported - no explicit imports needed in Vue files.

Named composables are available from @flyva/nuxt/composables (or the package root @flyva/nuxt). FlyvaPage, FlyvaLink, and other runtime components are available from @flyva/nuxt/components (or @flyva/nuxt) when you need an explicit import path.

Module Options

Configured under the flyva key in nuxt.config.ts:

ts
export default defineNuxtConfig({
  modules: ['@flyva/nuxt'],
  flyva: {
    defaultKey: 'defaultTransition',
    transitionsDir: 'page-transitions',
    useNamedExports: true,
    viewTransition: true,
  },
});
OptionTypeDefaultDescription
defaultKeystring'defaultTransition'Fallback map key after matchTransitionKey when no condition matches (and when the link omits flyva-transition)
transitionsDirstring'flyva-transitions'Directory containing transition files (relative to project root)
useNamedExportsbooleantrueIf true, each named export becomes a separate transition. If false, the default export is used.
viewTransitionbooleanundefinedEnables document.startViewTransition in FlyvaLink when the browser supports it

Components

FlyvaPage

Replaces <NuxtPage />. Wraps it with Vue's <Transition> (with css: false) and coordinates the leave/enter lifecycle with Nuxt's page hooks.

vue
<template>
  <NuxtLayout>
    <FlyvaPage />
  </NuxtLayout>
</template>

Internal hook bindings (simplified):

Nuxt hookRole
page:loading:startStarts the leave / enter promise coordination
page:startIf a transition is already running (prepare ran from FlyvaLink), runs sequential beforeLeaveleaveafterLeave; otherwise only resolves the leave gate (plain NuxtLink / :flyva="false"). Early exit for VT / CSS-only cases
page:finishRuns concurrent enter, CSS-mode completion, or sequential enter depending on the active transition

Vue <Transition> uses mode: 'out-in' for sequential transitions and undefined when concurrent or View Transitions are active so both roots can exist during the handoff.

Uses defineOptions({ inheritAttrs: false }) and v-bind="$attrs" internally, so any attrs you add to <FlyvaPage> are forwarded to <NuxtPage>.


Wraps NuxtLink with transition support. Auto-imported.

vue
<FlyvaLink
  to="/about"
  flyva-transition="slideTransition"
  :flyva-options="{ direction: 'left' }"
  @transitionStart="onStart"
>
  About
</FlyvaLink>
PropTypeDefaultDescription
flyva-transitionstring- (optional)When set, that map key runs. When omitted, conditions are evaluated, then flyva.defaultKey as fallback (see Writing transitions)
:flyva-optionsPageTransitionOptions | () => PageTransitionOptions{}Data passed to context.options
EventPayloadDescription
@transitionStartnoneEmitted before the transition begins

All NuxtLink props are accepted (to, href, external, prefetch, replace, etc.).

TIP

FlyvaLink accesses the underlying DOM element via the NuxtLink component's $el property. This is passed as context.el to your transition code.


Composables

All composables are auto-imported by the module.

useFlyvaTransition()

Returns the transition controller.

ts
const { prepare, isRunning, stage, hasTransitioned } = useFlyvaTransition();
FieldTypeDescription
prepare(name, options, el?)(string, PageTransitionOptions, Element?) => Promise<void>Start a transition by key
isRunningComputedRef<boolean>Reactive flag - true while a transition is active
stageComputedRef<PageTransitionStage>Current lifecycle stage (reactive)
hasTransitionedboolean (getter)true after the first transition

useFlyvaLifecycle(callbacks, options?)

Subscribe to transition lifecycle from any component. Uses useNuxtApp().$flyvaManager (same singleton as FlyvaPage / FlyvaLink). The composable always registers an active hook with the manager (including for prepare on run()), same as the Next adapter.

ts
useFlyvaLifecycle({
  beforeLeave(ctx) { console.log('leaving', ctx.name); },
  afterEnter(ctx) { console.log('entered', ctx.name); },
});

FlyvaLifecycleCallbacks:

CallbackTypeNotes
prepare(context: PageTransitionContext) => void | Promise<void>With blocking: false, the manager does not wait for returned promises
beforeLeave(context: PageTransitionContext) => voidSync only
leave(context: PageTransitionContext) => void | Promise<void>With blocking: true, awaited with the transition’s leave
afterLeave(context: PageTransitionContext) => voidSync only
beforeEnter(context: PageTransitionContext) => voidSync only
enter(context: PageTransitionContext) => void | Promise<void>With blocking: true, awaited with the transition’s enter
afterEnter(context: PageTransitionContext) => voidSync only
cleanup() => voidSync only; no context

UseFlyvaLifecycleOptions:

OptionTypeDefaultDescription
blockingbooleanfalseWhen false, prepare / leave / enter still run on the manager timeline but are not awaited. When true, those steps await your work (and can be cancelled on unmount).

Non-blocking mode (default, blocking: false)

Same behavior as the Next adapter: always registered as an active hook; only prepare / leave / enter skip awaiting when blocking is false.

Blocking mode (blocking: true)

prepare, leave, and enter return promises that the manager awaits in parallel with the transition implementation (Promise.all per stage).

Vue clears template refs before blocking leave may finish

On a page navigation, Nuxt tears down the old page while Flyva may still be inside leave. Vue sets template refs on that page to null as the DOM unmounts, so a normal ref<HTMLElement>() is often null inside an async blocking leave even though the transition has not finished.

Use useFlyvaStickyRef() (below) for elements you must read or animate from blocking lifecycle hooks. It keeps the last mounted node until Flyva runs active-hook unregister cleanup (after leave, when queued hook GC runs), then releases it.

Teleporting UI out of the animating subtree (e.g. to body) is another way to avoid losing the node, but sticky refs match how registerActiveHook teardown is ordered.

ts
const bar = useFlyvaStickyRef<HTMLDivElement>();

useFlyvaLifecycle(
  {
    async leave() {
      const el = bar.value;
      if (!el) return;
      await animate(el, { width: '100%', duration: 400 });
    },
  },
  { blocking: true },
);

useFlyvaStickyRef()

Returns a Ref<T | null> (default T is HTMLElement) intended for template refs on markup you touch from useFlyvaLifecycle with blocking: true.

  • Implemented with customRef: assigning null (Vue’s unmount reset) does not drop the stored element.
  • On mount, registers an empty registerActiveHook({}) so teardown participates in the same GC queue as other active hooks.
  • When that unregister cleanup runs, the ref is cleared to null and dependents update.
vue
<script setup lang="ts">
import { useFlyvaStickyRef } from '@flyva/nuxt/composables';

const bar = useFlyvaStickyRef<HTMLDivElement>();
</script>

<template>
  <div ref="bar">…</div>
</template>

Bind the returned ref on the element the same way as a normal Vue template ref.


useFlyvaState()

Used internally by FlyvaPage for leave/enter promise coordination. Prefer useFlyvaTransition for app-level transition control unless you are extending the adapter.


useRefStack(key, ref)

Registers a Vue ref in the global ref stack. Cleaned up automatically in onUnmounted.

vue
<script setup>
const hero = ref(null);
useRefStack('hero', hero);
</script>
ParamTypeDescription
keystringUnique identifier
refRef<MaybeElement>Vue ref to register

globalGetRefStackItem(key)

Retrieves a ref from the global stack. Returns Ref<T> | undefined.

ts
const hero = globalGetRefStackItem<HTMLElement>('hero');
if (hero?.value) { /* ... */ }

WARNING

In transition files (which run as virtual modules), use an explicit import path instead of relying on auto-imports:

ts
import { globalGetRefStackItem } from '@flyva/nuxt/composables';

globalGetRefStack()

Returns the entire ref stack as Record<string, Ref>.


useDetachedRoot(render)

Same idea as the Next adapter: mounts a one-off Vue app on a detached div appended to document.body. Returns { refs, waitForRender, destroy } where refs is a lazy ref() map. Use from transition classes (as in the playground overlay) or client-only code; call destroy() in cleanup().


Auto-imports summary

NameKindSource
FlyvaPageComponent@flyva/nuxt/components (or @flyva/nuxt)
FlyvaLinkComponent@flyva/nuxt/components (or @flyva/nuxt)
useFlyvaTransitionComposable@flyva/nuxt/composables
useFlyvaLifecycleComposable@flyva/nuxt/composables
useFlyvaStickyRefComposable@flyva/nuxt/composables
useFlyvaStateComposable@flyva/nuxt/composables
useRefStackComposable@flyva/nuxt/composables
useDetachedRootComposable@flyva/nuxt/composables
globalGetRefStackItemFunction@flyva/nuxt/composables
globalGetRefStackFunction@flyva/nuxt/composables