Skip to content

JS API

Wrap DOM updates in Motion's animateView() so the change plays as a view transition. A drop-in alternative to the ViewTransition component — same enter()/leave()/toggle() methods, state property, events and viewTransitionName/enterTo/leaveTo options — but the animation is declared with Motion keyframes and transitions (including springs) instead of the ::view-transition-* CSS pseudo-elements.

html
<div
  data-component="MotionView"
  data-option-enter-to="is-open"
  data-option-transition='{ "type": "spring", "bounce": 0.3 }'>

</div>

<button data-component="Action" data-on:click="MotionView->target.toggle()">Toggle</button>

Options

Object options are parsed as JSON: quote the keys (data-option-new='{ "opacity": [0, 1] }').

viewTransitionName

  • Type: string
  • Default: ''

Assigned as the element's view-transition-name on mount, exactly like ViewTransition. Optional with MotionView: animateView() names the subjects it animates automatically.

enterTo

  • Type: string
  • Default: ''

Classes describing the shown state. Added on enter, removed on leave.

leaveTo

  • Type: string
  • Default: ''

Classes describing the hidden state. Added on leave, removed on enter. Usually also the element's initial class so it starts hidden.

transition

  • Type: ViewTransitionOptions
  • Default: {}

The root animateView() options: a default transition (duration, ease, type: "spring", …) for every layer of the view transition.

add

  • Type: string
  • Default: ''

A selector resolved within the component's element: every matched element becomes an animated subject of the transition. When empty, the element itself is the subject.

new, old, enter, exit

  • Type: DOMKeyframesDefinition
  • Default: {}

Per-layer keyframes applied to each subject, mapping to the builder's new()/old()/enter()/exit() methods: new and old animate the new and old views whether the element persists or not, while enter and exit only fire for a pure newcomer or leaver.

layout

  • Type: boolean
  • Default: false

Enable the layout morph on each subject (the builder's layout()), so position and size changes animate smoothly.

auto

  • Type: boolean
  • Default: true

Enable ambient wiring: the component wraps any dom-update announced inside its subtree and joins the lifecycle of a containing Dialog. Opt out with data-option-no-auto.

Events

The same events as ViewTransition, in the same order: enter, enter-start, enter-end around the enter transition and leave, leave-start, leave-end around the leave transition.

Methods

MethodDescription
enter()Swap leaveTo for enterTo inside a view transition. Resolves once the animation settles.
leave()Swap enterTo for leaveTo inside a view transition. Resolves once the animation settles.
toggle()Toggle between enter and leave, entering first.
update(mutate)The underlying primitive: run any DOM mutation as a view transition configured by the options. Never rejects.

Ambient wiring

Containment is the wiring: with the auto option (on by default), a mounted MotionView listens for the bubbling dom-update event that mutating components — Fetch, DataBind's data-bind:if — announce before changing the DOM, and runs the announced change through update() so it plays as a view transition. Nesting the mutators inside the component is enough, with zero wiring attributes on either side:

html
<div data-component="MotionView" data-option-transition='{ "type": "spring", "bounce": 0.2 }'>
  <form action="/search" data-component="Fetch">
    <input type="search" name="q" />
  </form>

  <template data-component="DataBind" data-option-key="expanded" data-bind:if>
    <p>…</p>
  </template>
</div>

A MotionView placed inside a Dialog also joins its lifecycle: the dialog's extendable open and close events bubble past the component, which hands itself to detail.waitUntil() — the dialog then awaits enter() on open and leave() on close.

Opt out with data-option-no-auto. Explicit wiring through Action remains for cross-subtree topologies, where the mutator and the animated subtree are not nested:

html
<form
  action="/search"
  data-component="Fetch Action"
  data-on:dom-update="MotionView(#list)->event.detail.wrap(target)">
  <input type="search" name="q" />
</form>

<ul id="list" data-component="MotionView">

</ul>

Notes

  • The mutation is never lost: in browsers without the View Transitions API — or when the animation rejects — the update still applies, only without animation.
  • animateView() is not part of motion/mini: when the provided module lacks it, the component warns and applies updates directly.