
Motion
- 2.1k installs
- 696 repo stars
- Updated July 27, 2026
- onmax/nuxt-skills
motion is an agent skill that Use when adding animations with Motion Vue (motion-v) - provides motion component API, gesture animations, scroll-linked.
About
Animation library for Vue 3 and Nuxt Production ready hardware accelerated animations with minimal bundle size Current stable motion v 1 x Vue port of Motion formerly Framer Motion Progressive reference for Motion Vue animations Load only files relevant to current task 200 tokens base 500 1500 per sub file Simple declarative animations fade slide scale Gesture based interactions hover tap drag Scroll linked animations Layout animations and shared element transitions Spring physics animations GSAP Complex timelines SVG morphing scroll triggered sequences vueuse motion Simpler API less features smaller bundle CSS animations Simple transitions without JS The motion agent skill provides documented workflows prerequisites triggers and safety guidance from its SKILL md source Agents load it when user requests match the description and follow step by step instructions without inventing capabilities It integrates with standard agent tooling for the tasks inputs outputs and failure modes described in the repository documentation
- description: Use when adding animations with Motion Vue (motion-v) - provides motion component API, gesture animations,
- Animation library for Vue 3 and Nuxt. Production-ready, hardware-accelerated animations with minimal bundle size.
- **Current stable:** motion-v 1.x - Vue port of Motion (formerly Framer Motion)
- Follow motion SKILL.md steps and documented constraints.
- Follow motion SKILL.md steps and documented constraints.
Motion by the numbers
- 2,091 all-time installs (skills.sh)
- +47 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #574 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 4, 2026 (Skillselion catalog sync)
motion capabilities & compatibility
- Capabilities
- description: use when adding animations with mot · animation library for vue 3 and nuxt. production · **current stable:** motion v 1.x vue port of m · follow motion skill.md steps and documented cons
- Use cases
- orchestration
What motion says it does
description: Use when adding animations with Motion Vue (motion-v) - provides motion component API, gesture animations, scroll-linked effects, layout transitions, and composables for Vue 3/Nuxt
Animation library for Vue 3 and Nuxt. Production-ready, hardware-accelerated animations with minimal bundle size.
**Current stable:** motion-v 1.x - Vue port of Motion (formerly Framer Motion)
npx skills add https://github.com/onmax/nuxt-skills --skill motionAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2.1k |
|---|---|
| repo stars | ★ 696 |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 27, 2026 |
| Repository | onmax/nuxt-skills ↗ |
When should an agent use motion and what problem does it solve?
Use when adding animations with Motion Vue (motion-v) - provides motion component API, gesture animations, scroll-linked effects, layout transitions, and composables for Vue 3/Nuxt
Who is it for?
Developers invoking motion as documented in the skill source.
Skip if: Skip when requirements fall outside motion documented scope.
When should I use this skill?
Use when adding animations with Motion Vue (motion-v) - provides motion component API, gesture animations, scroll-linked effects, layout transitions, and composables for Vue 3/Nuxt
What you get
Outputs aligned with the motion SKILL.md workflow and stated deliverables.
- motion component configs
- gesture handlers
- scroll-linked animation code
By the numbers
- motion-v 1.x is the current stable Motion Vue release documented by the skill
- Progressive reference loads ~200 tokens base and 500–1500 tokens per sub-file
Files
Motion Vue (motion-v)
Animation library for Vue 3 and Nuxt. Production-ready, hardware-accelerated animations with minimal bundle size.
Current stable: motion-v 1.x - Vue port of Motion (formerly Framer Motion)
Overview
Progressive reference for Motion Vue animations. Load only files relevant to current task (~200 tokens base, 500-1500 per sub-file).
When to Use
Use Motion Vue for:
- Simple declarative animations (fade, slide, scale)
- Gesture-based interactions (hover, tap, drag)
- Scroll-linked animations
- Layout animations and shared element transitions
- Spring physics animations
Consider alternatives:
- GSAP - Complex timelines, SVG morphing, scroll-triggered sequences
- @vueuse/motion - Simpler API, less features, smaller bundle
- CSS animations - Simple transitions without JS
Installation
# Vue 3
pnpm add motion-v
# Nuxt 3
pnpm add motion-v @vueuse/nuxt// nuxt.config.ts - Nuxt 3 setup
export default defineNuxtConfig({
modules: ['motion-v/nuxt'],
})Quick Reference
| Working on... | Load file |
|---|---|
| Motion component, gestures | references/components.md |
| useMotionValue, useScroll | references/composables.md |
| Animation examples, patterns | references/examples.md |
Loading Files
Consider loading these reference files based on your task:
- [ ] references/components.md - if using Motion component, gestures, or layout animations
- [ ] references/composables.md - if using useMotionValue, useScroll, useSpring, or animate()
- [ ] references/examples.md - if looking for animation patterns or inspiration
DO NOT load all files at once. Load only what's relevant to your current task.
Core Concepts
Motion Component
Render any HTML/SVG element with animation capabilities:
<script setup lang="ts">
import { motion } from 'motion-v'
</script>
<template>
<motion.div
:initial="{ opacity: 0, y: 20 }"
:animate="{ opacity: 1, y: 0 }"
:exit="{ opacity: 0, y: -20 }"
:transition="{ duration: 0.3 }"
>
Animated content
</motion.div>
</template>Gesture Animations
<motion.button
:whileHover="{ scale: 1.05 }"
:whilePress="{ scale: 0.95 }"
:transition="{ type: 'spring', stiffness: 400 }"
>
Click me
</motion.button>Scroll Animations
<motion.div
:initial="{ opacity: 0 }"
:whileInView="{ opacity: 1 }"
:viewport="{ once: true, margin: '-100px' }"
>
Appears on scroll
</motion.div>Available Guidance
[references/components.md](references/components.md) - Motion component variants, animation props, gesture props, layout animations, transition configuration
[references/composables.md](references/composables.md) - useMotionValue, useSpring, useTransform, useScroll, useInView, animate()
[references/examples.md](references/examples.md) - External resources, component libraries, animation patterns and inspiration
Motion Component API
Import Patterns
motion (lowercase) - Standard pattern
The standard way to use motion-v with dot notation:
<script setup>
import { motion } from 'motion-v'
</script>
<template>
<motion.div />
</template>Motion (capital) - Primitive pattern
Alternative pattern using as prop. Useful when element type needs to be dynamic at runtime:
<script setup>
import { Motion } from 'motion-v'
</script>
<template>
<Motion as="div" />
</template>Use `motion` (lowercase) throughout this guide - it's preloaded with all features.
Component Variants
The motion component wraps any HTML or SVG element:
<script setup lang="ts">
import { motion } from 'motion-v'
</script>
<template>
<!-- HTML elements -->
<motion.div />
<motion.span />
<motion.button />
<motion.a />
<motion.img />
<motion.ul />
<motion.li />
<!-- SVG elements -->
<motion.svg />
<motion.path />
<motion.circle />
<motion.rect />
<motion.g />
</template>Animation Props
initial
Starting state before component mounts:
<motion.div :initial="{ opacity: 0, scale: 0.8, x: -100 }">Set initial: false to disable enter animation and start at animate values.
animate
Target animation state. Changes trigger animation:
<script setup>
const isOpen = ref(false)
</script>
<motion.div :animate="{ height: isOpen ? 'auto' : 0 }">exit
Animation when component unmounts. Requires AnimatePresence:
<script setup>
import { motion, AnimatePresence } from 'motion-v'
</script>
<template>
<AnimatePresence>
<motion.div
v-if="show"
:initial="{ opacity: 0 }"
:animate="{ opacity: 1 }"
:exit="{ opacity: 0 }"
/>
</AnimatePresence>
</template>style
Reactive style object. Use motion values for performant updates:
<motion.div :style="{ x: motionValue, backgroundColor: '#fff' }">Gesture Props
whileHover
Animation while pointer hovers:
<motion.div
:whileHover="{ scale: 1.1, backgroundColor: '#f00' }"
:transition="{ type: 'spring', stiffness: 300 }"
/>whilePress / whileTap
Animation while element is pressed:
<motion.button :whilePress="{ scale: 0.95 }">whileFocus
Animation while element has focus:
<motion.input :whileFocus="{ borderColor: '#0066ff', scale: 1.02 }">whileDrag
Animation while element is being dragged:
<motion.div
drag
:whileDrag="{ scale: 1.1, cursor: 'grabbing' }"
:dragConstraints="{ top: 0, left: 0, right: 300, bottom: 300 }"
/>Drag Props
<motion.div
drag <!-- Enable drag on both axes -->
drag="x" <!-- Constrain to x-axis -->
drag="y" <!-- Constrain to y-axis -->
:dragConstraints="{ top: -50, left: -50, right: 50, bottom: 50 }"
:dragElastic="0.2" <!-- 0 = rigid, 1 = elastic -->
:dragMomentum="true" <!-- Continue with momentum -->
:dragSnapToOrigin="true" <!-- Return to start -->
/>Viewport Props
whileInView
Animation when element enters viewport:
<motion.div
:initial="{ opacity: 0, y: 50 }"
:whileInView="{ opacity: 1, y: 0 }"
:viewport="{ once: true, amount: 0.5 }"
/>viewport Options
interface ViewportOptions {
once?: boolean // Animate only first time (default: false)
amount?: number | 'some' | 'all' // Visibility threshold (default: 'some')
margin?: string // Rootmargin (e.g., '-100px')
root?: Element // Scroll container (default: window)
}Layout Animation
layout Prop
Automatically animate layout changes:
<motion.div layout>
<!-- Position/size changes animate smoothly -->
</motion.div>Layout modes:
<motion.div layout /> <!-- Animate position and size -->
<motion.div layout="position" /> <!-- Position only -->
<motion.div layout="size" /> <!-- Size only -->layoutId - Shared Element Transitions
Animate between components with matching layoutId:
<script setup>
const selected = ref<string | null>(null)
</script>
<template>
<div class="grid">
<motion.div
v-for="item in items"
:key="item.id"
:layoutId="item.id"
@click="selected = item.id"
/>
</div>
<AnimatePresence>
<motion.div
v-if="selected"
:layoutId="selected"
class="expanded"
/>
</AnimatePresence>
</template>LayoutGroup
Sync layout animations across components:
<script setup>
import { motion, LayoutGroup } from 'motion-v'
</script>
<template>
<LayoutGroup>
<motion.div layout />
<motion.div layout />
</LayoutGroup>
</template>Transition Configuration
transition Prop
Configure animation behavior:
<motion.div
:animate="{ x: 100 }"
:transition="{
type: 'spring',
stiffness: 100,
damping: 10,
mass: 1,
}"
/>Transition Types
Spring (default):
{
type: 'spring',
stiffness: 100, // Higher = snappier (default: 100)
damping: 10, // Higher = less bounce (default: 10)
mass: 1, // Higher = slower (default: 1)
velocity: 0, // Initial velocity
restDelta: 0.01, // End threshold
}Tween:
{
type: 'tween',
duration: 0.3,
ease: 'easeOut', // 'linear' | 'easeIn' | 'easeOut' | 'easeInOut' | [0.42, 0, 0.58, 1]
delay: 0,
repeat: 0, // Number of repeats (Infinity for loop)
repeatType: 'loop', // 'loop' | 'reverse' | 'mirror'
repeatDelay: 0,
}Inertia (for drag):
{
type: 'inertia',
velocity: 50,
power: 0.8,
timeConstant: 700,
bounceStiffness: 500,
bounceDamping: 10,
}Per-Property Transitions
<motion.div
:animate="{ x: 100, opacity: 1 }"
:transition="{
x: { type: 'spring', stiffness: 100 },
opacity: { duration: 0.2 },
}"
/>AnimatePresence
Animate components as they mount/unmount:
<script setup>
import { motion, AnimatePresence } from 'motion-v'
</script>
<template>
<AnimatePresence mode="wait">
<motion.div
:key="currentPage"
:initial="{ opacity: 0, x: 100 }"
:animate="{ opacity: 1, x: 0 }"
:exit="{ opacity: 0, x: -100 }"
/>
</AnimatePresence>
</template>Mode Options
<AnimatePresence mode="sync" /> <!-- Default: animate simultaneously -->
<AnimatePresence mode="wait" /> <!-- Wait for exit before enter -->
<AnimatePresence mode="popLayout" /> <!-- Pop exiting from layout flow -->Events
<motion.div
@animationStart="onStart"
@animationComplete="onComplete"
@update="onUpdate" <!-- Called every frame -->
@hoverStart="onHoverStart"
@hoverEnd="onHoverEnd"
@pressStart="onPressStart"
@pressEnd="onPressEnd"
@dragStart="onDragStart"
@drag="onDrag"
@dragEnd="onDragEnd"
@viewportEnter="onEnter"
@viewportLeave="onLeave"
/>Motion Composables
useMotionValue
Create a motion value for performant animations that bypass Vue's reactivity:
<script setup>
import { motion, useMotionValue } from 'motion-v'
const x = useMotionValue(0)
// Read/write value
console.log(x.get())
x.set(100)
// Subscribe to changes
const unsubscribe = x.on('change', (latest) => {
console.log('x changed to', latest)
})
</script>
<template>
<motion.div :style="{ x }" />
</template>useSpring
Create spring-animated motion value:
<script setup>
import { motion, useMotionValue, useSpring } from 'motion-v'
const x = useMotionValue(0)
const springX = useSpring(x, {
stiffness: 100,
damping: 10,
mass: 1,
})
// springX follows x with spring physics
x.set(100)
</script>
<template>
<motion.div :style="{ x: springX }" />
</template>useTransform
Derive motion values from other motion values:
<script setup>
import { motion, useMotionValue, useTransform } from 'motion-v'
const x = useMotionValue(0)
// Map x: 0-100 to opacity: 1-0
const opacity = useTransform(x, [0, 100], [1, 0])
// Map x: 0-100 to scale: 1-2
const scale = useTransform(x, [0, 100], [1, 2])
// Custom transform function
const rotate = useTransform(x, (value) => `${value}deg`)
// Combine multiple values
const combined = useTransform([x, opacity], ([x, opacity]) => {
return x * opacity
})
</script>
<template>
<motion.div :style="{ x, opacity, scale, rotate }" />
</template>Easing in useTransform
import { easeInOut } from 'motion-v'
const opacity = useTransform(x, [0, 100], [0, 1], { ease: easeInOut })useMotionTemplate
Create template strings from motion values:
<script setup>
import { useMotionValue, useMotionTemplate } from 'motion-v'
const x = useMotionValue(0)
const y = useMotionValue(0)
// Creates "translateX(0px) translateY(0px)"
const transform = useMotionTemplate`translateX(${x}px) translateY(${y}px)`
// For gradients, shadows, etc.
const blur = useMotionValue(5)
const filter = useMotionTemplate`blur(${blur}px)`
</script>useScroll
Track scroll progress of window or element:
<script setup>
import { useScroll, useTransform, motion } from 'motion-v'
// Window scroll
const { scrollX, scrollY, scrollXProgress, scrollYProgress } = useScroll()
// Element scroll
const containerRef = ref<HTMLElement>()
const { scrollYProgress: containerProgress } = useScroll({
container: containerRef,
})
// Element in viewport progress
const targetRef = ref<HTMLElement>()
const { scrollYProgress: targetProgress } = useScroll({
target: targetRef,
offset: ['start end', 'end start'], // When tracking starts/ends
})
// Transform scroll to animation values
const opacity = useTransform(scrollYProgress, [0, 0.5], [0, 1])
const scale = useTransform(scrollYProgress, [0, 1], [0.8, 1])
</script>
<template>
<motion.div :style="{ opacity, scale }" />
</template>Scroll Offset Options
offset: ['start end', 'end start']
// First: target position relative to container
// Second: container position relative to viewport
// Common patterns:
['start end', 'end start'] // Element enters bottom, exits top
['start start', 'end start'] // Pin at top while scrolling
['center center', 'end start'] // Centered animationuseInView
Detect element visibility in viewport:
<script setup>
import { useInView } from 'motion-v'
const ref = ref<HTMLElement>()
const isInView = useInView(ref, {
once: true, // Only trigger once
amount: 0.5, // 50% visible to trigger
margin: '-100px', // Shrink viewport detection
})
watch(isInView, (inView) => {
if (inView) console.log('Element visible')
})
</script>
<template>
<div ref="ref">Tracked element</div>
</template>useAnimationFrame
Run callback every frame with delta time:
<script setup>
import { useMotionValue, useAnimationFrame } from 'motion-v'
const rotation = useMotionValue(0)
useAnimationFrame((time, delta) => {
// time: total elapsed ms
// delta: ms since last frame
rotation.set(rotation.get() + delta * 0.1)
})
</script>
<template>
<motion.div :style="{ rotate: rotation }">Spinning</motion.div>
</template>animate()
Imperative animation function:
import { animate } from 'motion-v'
// Animate value
const controls = animate(0, 100, {
duration: 0.5,
onUpdate: (latest) => console.log(latest),
onComplete: () => console.log('done'),
})
// Control animation
controls.stop()
controls.time = 0.25 // Seek to 25%
// Animate motion value
const x = useMotionValue(0)
animate(x, 100, { type: 'spring' })
// Animate object
animate(
{ x: 0, y: 0 },
{ x: 100, y: 100 },
{
duration: 1,
onUpdate: ({ x, y }) => console.log(x, y),
}
)Sequence Animations
import { animate, stagger } from 'motion-v'
// Animate elements in sequence
const elements = document.querySelectorAll('.item')
animate(elements, { opacity: 1, y: 0 }, {
delay: stagger(0.1), // 0.1s between each
duration: 0.5,
})
// Custom stagger
animate(elements, { opacity: 1 }, {
delay: stagger(0.1, {
start: 0.5, // Start delay
from: 'center', // 'first' | 'last' | 'center' | number
ease: 'easeOut',
}),
})useVelocity
Track velocity of motion value:
<script setup>
import { useMotionValue, useVelocity, useTransform } from 'motion-v'
const x = useMotionValue(0)
const xVelocity = useVelocity(x)
// Skew based on velocity
const skewX = useTransform(xVelocity, [-1000, 0, 1000], [-25, 0, 25])
</script>useReducedMotion
Respect user's motion preferences:
<script setup>
import { useReducedMotion } from 'motion-v'
const prefersReduced = useReducedMotion()
</script>
<template>
<motion.div
:animate="{ x: 100 }"
:transition="prefersReduced ? { duration: 0 } : { type: 'spring' }"
/>
</template>Animation Examples & Resources
Links to external animation libraries, component collections, and inspiration.
Official Motion Resources
- [motion.dev/examples](https://motion.dev/examples) - Official Motion examples gallery
- [github.com/motiondivision/motion-vue](https://github.com/motiondivision/motion-vue) - Motion Vue source code
Vue Animation Libraries
- [Vue Bits](https://github.com/DavidHDev/vue-bits) - 110+ animated Vue components (text reveals, backgrounds, UI effects)
- [Inspira UI](https://inspira-ui.com/) - Beautiful UI components built with Vue, Nuxt, and Tailwind CSS
Animation Pattern References
These React/CSS libraries provide patterns easily ported to Motion Vue:
- [React Bits](https://github.com/DavidHDev/react-bits) - Same author as Vue Bits, patterns translate directly
- [Magic UI](https://github.com/magicuidesign/magicui) - 150+ landing page effects, shadcn companion
- [Animata](https://github.com/codse/animata) - 80+ Tailwind copy-paste animations
- [Aceternity UI](https://ui.aceternity.com) - Premium animations (lamp, globe, 3D cards)
- [Motion Primitives](https://motion-primitives.com) - Animated UI component kit
- [Cult UI](https://www.cult-ui.com/) - Curated animated components
Patterns by Category
Scroll Animations
<!-- Parallax -->
<script setup>
import { motion, useScroll, useTransform } from 'motion-v'
const { scrollYProgress } = useScroll()
const y = useTransform(scrollYProgress, [0, 1], [0, -200])
</script>
<motion.div :style="{ y }">Parallax content</motion.div><!-- Progress bar -->
<motion.div
class="fixed top-0 left-0 right-0 h-1 bg-blue-500 origin-left"
:style="{ scaleX: scrollYProgress }"
/>Layout Animations
<!-- Expanding card -->
<motion.div
:layoutId="`card-${id}`"
@click="expanded = !expanded"
:class="expanded ? 'w-full h-64' : 'w-32 h-32'"
/><!-- Reordering list -->
<motion.li
v-for="item in items"
:key="item.id"
layout
:initial="{ opacity: 0 }"
:animate="{ opacity: 1 }"
:exit="{ opacity: 0 }"
/>Gesture Interactions
<!-- Magnetic button -->
<script setup>
import { motion, useMotionValue } from 'motion-v'
const x = useMotionValue(0)
const y = useMotionValue(0)
function handleMouse(e: MouseEvent) {
const rect = (e.target as HTMLElement).getBoundingClientRect()
x.set(e.clientX - rect.left - rect.width / 2)
y.set(e.clientY - rect.top - rect.height / 2)
}
function reset() {
x.set(0)
y.set(0)
}
</script>
<motion.button
:style="{ x, y }"
@mousemove="handleMouse"
@mouseleave="reset"
:transition="{ type: 'spring', stiffness: 150 }"
/><!-- Swipe to dismiss -->
<motion.div
drag="x"
:dragConstraints="{ left: 0, right: 0 }"
:onDragEnd="(_, info) => info.offset.x > 100 && dismiss()"
:style="{ x }"
/>Text Animations
<!-- Staggered text reveal -->
<script setup>
import { motion } from 'motion-v'
const words = text.split(' ')
</script>
<template>
<span v-for="(word, i) in words" :key="i">
<motion.span
:initial="{ opacity: 0, y: 20 }"
:animate="{ opacity: 1, y: 0 }"
:transition="{ delay: i * 0.1 }"
>
{{ word }}
</motion.span>
{{ ' ' }}
</span>
</template><!-- Character animation -->
<motion.span
v-for="(char, i) in text.split('')"
:key="i"
:initial="{ opacity: 0 }"
:animate="{ opacity: 1 }"
:transition="{ delay: i * 0.03 }"
>
{{ char }}
</motion.span>SVG Animations
<!-- Path drawing -->
<motion.path
d="M0 0 L100 100"
:initial="{ pathLength: 0 }"
:animate="{ pathLength: 1 }"
:transition="{ duration: 2, ease: 'easeInOut' }"
/><!-- Morphing shapes -->
<motion.path
:animate="{ d: isCircle ? circlePath : squarePath }"
:transition="{ duration: 0.5 }"
/>Background Effects
<!-- Gradient follow cursor -->
<script setup>
import { motion, useMotionValue, useMotionTemplate } from 'motion-v'
const x = useMotionValue(0)
const y = useMotionValue(0)
const background = useMotionTemplate`radial-gradient(circle at ${x}px ${y}px, #3b82f6, transparent 80%)`
function handleMouse(e: MouseEvent) {
x.set(e.clientX)
y.set(e.clientY)
}
</script>
<motion.div
:style="{ background }"
@mousemove="handleMouse"
class="fixed inset-0"
/>Page Transitions
<!-- Slide transition -->
<AnimatePresence mode="wait">
<motion.div
:key="route.path"
:initial="{ opacity: 0, x: 100 }"
:animate="{ opacity: 1, x: 0 }"
:exit="{ opacity: 0, x: -100 }"
:transition="{ type: 'spring', stiffness: 100, damping: 20 }"
/>
</AnimatePresence>Performance Tips
1. Use motion values for frequent updates (mouse position, scroll) 2. Prefer transform properties (x, y, scale, rotate) over layout properties (width, height, top, left) 3. Use `layout` sparingly - it measures DOM which can be expensive 4. Set `once: true` on viewport animations when possible 5. Use `will-change` for complex animations (Motion adds this automatically)
Related skills
How it compares
Choose motion over generic CSS animation skills when you need Motion Vue composables, gestures, and layout transitions in Vue 3 or Nuxt rather than keyframe-only styling.
FAQ
What is motion?
Use when adding animations with Motion Vue (motion-v) - provides motion component API, gesture animations, scroll-linked effects, layout transitions, and composables for Vue 3/Nuxt
When should I use motion?
Use when adding animations with Motion Vue (motion-v) - provides motion component API, gesture animations, scroll-linked effects, layout transitions, and composables for Vue 3/Nuxt
Is motion safe to install?
Review the Security Audits panel on this page before production use.