Next.js + Motion/Framer Motion
Mission
Build small, purposeful, accessible animations in Next.js using Motion for React (the current package) or legacy framer-motion, without breaking server/client boundaries, performance, or usability.
Use this skill for
- First-render reveals and section entrances
- Hover, tap, and focus feedback on buttons, links, cards, tabs, and navigation
- Scroll-triggered reveals and modest scroll-linked effects
- Modals, drawers, dropdowns, accordions, tabs, and other enter/exit UI
- Layout and shared-element transitions with
layout and layoutId
- Reorderable lists and light route-content transitions
- Debugging Motion behaviour in Next.js
Do not use this skill for
- GSAP-style timelines or cinematic sequences
- Canvas, WebGL, Three.js, or Lottie-led animation systems
- Heavy parallax or scroll-jacking storytelling
- Large creative-direction rewrites
- Pure CSS effects that do not justify client JavaScript, unless Motion is explicitly requested
Non-negotiables
- Prefer the lightest Motion API that solves the task.
- Preserve repo consistency. Do not mix
motion and framer-motion imports in the same diff unless the task is an explicit migration.
- Keep animated logic in the smallest possible Client Component boundary.
- Respect reduced motion globally with
MotionConfig reducedMotion="user" and locally with useReducedMotion() when behaviour must change.
- Prefer reusable primitives, variants, and motion tokens over repeated inline animation objects.
- Do not add a global provider, root-layout Client Component, or route-wide animation system unless the request genuinely needs it.
Default workflow
1) Audit the codebase first
Inspect:
- Router type:
app/, pages/, or both.
- Current package:
motion, framer-motion, or neither.
- Existing animation patterns and design-system components.
- Candidate transition boundaries:
app/layout.tsx, app/template.tsx, pages/_app.tsx, shared UI shells.
- Whether the change is local animation, mount/unmount animation, layout animation, shared-element animation, reorder, or scroll-linked animation.
If shell access is available, run:
node scripts/audit-nextjs-motion.mjs --root /path/to/repo
node scripts/inspect-motion-target.mjs path/to/target-file.tsx --root /path/to/repo
node scripts/plan-motion-change.mjs --root /path/to/repo --target path/to/target-file.tsx --task "user request"
For broad skill iteration or repo-health checks, also run:
node scripts/check-motion-antipatterns.mjs --root /path/to/repo
2) Choose a package strategy
Default rules:
- New work or modernised motion layer: prefer the current
motion package with imports from motion/react.
- Existing repo already on
framer-motion: stay consistent unless the task explicitly includes migration.
- Passive App Router component with no hooks or client-only logic:
motion/react-client can be appropriate, but it is an exception, not the default.
- Leaf animations with hooks, route state, presence, reorder, or interactivity: use a small Client Component boundary.
See references/MIGRATION.md and references/DECISION_TREE.md.
3) Choose the lightest correct API
Use this decision rule:
- Simple local animation:
motion.*
- Bundle-sensitive shared shell:
m.* with LazyMotion
- Repeated parent/child orchestration: variants plus
stagger
- Mount/unmount or route exits:
AnimatePresence
- Layout changes from React re-render:
layout
- Shared-element transition:
layoutId
- Sibling layout coordination or namespaced shared layout IDs:
LayoutGroup
- Simple scroll reveal:
whileInView
- Scroll-linked progress or parallax:
useScroll plus motion values
- Imperative sequence or external trigger:
useAnimate
- Design-system component wrapper:
motion.create() with ref forwarding
See references/EXPERT_PLAYBOOK.md and references/DECISION_TREE.md.
4) Wire it correctly for the router
App Router
- Passive, hook-free animation in a server-friendly file: consider
motion/react-client.
- Interactive or hook-driven UI: create a small Client Component leaf and keep data fetching server-side.
- Client wrapper around server-rendered children: useful for modal shells, drawers, and local visibility wrappers.
- Route enter/exit choreography: mount a persistent Client shell from a layout so
AnimatePresence stays mounted.
- Segment replay on navigation:
template.tsx is useful when you want remount semantics at a specific segment boundary.
See references/APP_ROUTER.md.
Pages Router
- Keep
AnimatePresence stable in pages/_app.tsx for route transitions.
- Key routed children by a stable value that changes when you actually want a transition. For dynamic routes,
router.asPath is usually safer than router.route.
- Do not rewrite
_app.tsx for a one-off local animation.
See references/PAGES_ROUTER.md.
5) Apply the motion budget
Default ranges unless the user or design system says otherwise:
- Micro-interactions: 0.12s to 0.22s, scale no larger than 1.03, travel no more than 4px.
- Reveal / list entrance: 0.18s to 0.35s, travel 8px to 24px.
- Page / route transition: 0.22s to 0.45s, mostly opacity plus small Y translation.
- Layout animation: prefer Motion springs or
layout; do not fake these with large manual transforms.
See references/EXPERT_PLAYBOOK.md and references/PERFORMANCE.md.
6) Validate before finishing
Always check:
- No server/client boundary mistakes
- Reduced motion works
- Focus is preserved for interactive UI
- No unnecessary layout shift or stretched content
- No duplicate or conflicting route wrappers
- Project still builds
Run repo checks when available:
npm run lint
npm run build
For repo audits or skill iteration, also run:
node scripts/check-motion-antipatterns.mjs --root /path/to/repo
Use references/CHECKLIST.md before finalising. When improving the skill itself, use references/EVALUATION.md and node scripts/run-evaluation-pack.mjs.
Implementation rules
- Prefer animating transform and opacity. Avoid animating
top, left, large filters, and large shadows on big surfaces.
- When possible, turn the existing root element into a Motion element instead of adding a new wrapper. Extra wrappers often break layout, refs, selectors, or spacing.
- If using
m plus LazyMotion, use:
domAnimation for standard animations, variants, exit, hover, tap, and focus.
domMax only when you need layout animations or drag/pan.
- Use
AnimatePresence initial={false} for app-level wrappers unless first-load animation is explicitly desired.
- Use
AnimatePresence mode="wait" only when a single child should fully exit before the next enters.
- Never key exit-sensitive children by array index.
- Start with
layout before manual height choreography. If content stretches, add layout to the affected children or switch to layout="position" for aspect-ratio changes.
- If the scroll container is not the window, configure
viewport.root or use useInView with the correct root.
- When animating
next/image or image wrappers, preserve the layout box and animate transform or opacity rather than intrinsic size.
- When wrapping design-system components, call
motion.create() outside render and make sure the wrapped component forwards its ref.
- If the user did not explicitly ask for Motion and the effect is just a tiny hover or focus style on a static server-rendered element, CSS may be the cleaner answer.
Scripts
scripts/audit-nextjs-motion.mjs - inspects a repo, ranks likely target files, and emits JSON recommendations.
scripts/inspect-motion-target.mjs - inspects one file and recommends boundary, import path, risks, and likely pattern fit.
scripts/plan-motion-change.mjs - combines repo audit, target inspection, and task wording into a structured expert plan.
scripts/check-motion-antipatterns.mjs - scans a repo for common Motion and Framer Motion anti-patterns and emits JSON findings.
scripts/run-evaluation-pack.mjs - runs the bundled fixture-and-golden evaluation pack for skill iteration.
scripts/scaffold-motion-primitives.mjs - copies template components from assets/ into a target directory, with optional import rewriting for legacy framer-motion.
Reference map
references/EXPERT_PLAYBOOK.md - API selection, heuristics, motion tokens, anti-patterns
references/DECISION_TREE.md - fast pattern and boundary selection
references/APP_ROUTER.md - App Router boundaries, route shells, template.tsx, and server-friendly patterns
references/PAGES_ROUTER.md - _app.tsx, keys, dynamic route nuances
references/RECIPES.md - copy/paste implementations
references/PERFORMANCE.md - bundle size, LazyMotion, layout and scroll performance
references/ACCESSIBILITY.md - reduced motion, focus, modal guidance
references/MIGRATION.md - framer-motion to motion package strategy
references/TROUBLESHOOTING.md - failure modes and fixes
references/CHECKLIST.md - final review before finishing
references/EVALUATION.md - trigger tests, scenario fixtures, anti-pattern scans, and golden-output review
Output expectations
When modifying a repo, finish with:
- The files changed
- The Motion API and pattern chosen
- Boundary strategy and package migration decision, if any
- Reduced-motion handling
- Performance or bundle-size choices, if relevant
- Manual validation notes or commands run
- Any caveats the developer should know
Typical request mapping
- "Make this button feel better" -> micro-interaction recipe
- "Animate cards in on scroll" -> reveal recipe; variants if staggered
- "Add a smooth route transition in App Router" -> persistent layout-mounted shell or content wrapper, not a root rewrite by default
- "Animate this accordion or tab underline" ->
layout / layoutId / LayoutGroup
- "Make this drag list reorder smoothly" ->
Reorder.Group / Reorder.Item
- "This breaks in App Router" -> inspect boundary and import path before editing
- "Modernise our Framer Motion setup" -> audit first, then use
references/MIGRATION.md
1---2name: nextjs-framer-motion-animations3description: Adds production-safe Motion for React or Framer Motion animations to Next.js apps, including reveal, hover and tap micro-interactions, whileInView, stagger, AnimatePresence, layout and layoutId transitions, reorder, scroll-linked UI, and lightweight route-content transitions. Use when the user asks to add, refactor, or debug Motion or Framer Motion in App Router or Pages Router codebases, especially around server/client boundaries, reduced motion, LazyMotion, bundle size, hydration, or route transitions. Avoid for GSAP-style timelines, WebGL or 3D scenes, heavy scroll storytelling, or CSS-only effects unless Motion is explicitly requested.4license: MIT5---6
7# Next.js + Motion/Framer Motion
8
9## Mission
10Build small, purposeful, accessible animations in Next.js using Motion for React (the current package) or legacy `framer-motion`, without breaking server/client boundaries, performance, or usability.
11
12## Use this skill for
13- First-render reveals and section entrances
14- Hover, tap, and focus feedback on buttons, links, cards, tabs, and navigation
15- Scroll-triggered reveals and modest scroll-linked effects
16- Modals, drawers, dropdowns, accordions, tabs, and other enter/exit UI
17- Layout and shared-element transitions with `layout` and `layoutId`
18- Reorderable lists and light route-content transitions
19- Debugging Motion behaviour in Next.js
20
21## Do not use this skill for
22- GSAP-style timelines or cinematic sequences
23- Canvas, WebGL, Three.js, or Lottie-led animation systems
24- Heavy parallax or scroll-jacking storytelling
25- Large creative-direction rewrites
26- Pure CSS effects that do not justify client JavaScript, unless Motion is explicitly requested
27
28## Non-negotiables
29- Prefer the lightest Motion API that solves the task.
30- Preserve repo consistency. Do not mix `motion` and `framer-motion` imports in the same diff unless the task is an explicit migration.
31- Keep animated logic in the smallest possible Client Component boundary.
32- Respect reduced motion globally with `MotionConfig reducedMotion="user"` and locally with `useReducedMotion()` when behaviour must change.
33- Prefer reusable primitives, variants, and motion tokens over repeated inline animation objects.
34- Do not add a global provider, root-layout Client Component, or route-wide animation system unless the request genuinely needs it.
35
36## Default workflow
37
38### 1) Audit the codebase first
39Inspect:
40- Router type: `app/`, `pages/`, or both.
41- Current package: `motion`, `framer-motion`, or neither.
42- Existing animation patterns and design-system components.
43- Candidate transition boundaries: `app/layout.tsx`, `app/template.tsx`, `pages/_app.tsx`, shared UI shells.
44- Whether the change is local animation, mount/unmount animation, layout animation, shared-element animation, reorder, or scroll-linked animation.
45
46If shell access is available, run:
47```bash
48node scripts/audit-nextjs-motion.mjs --root /path/to/repo
49node scripts/inspect-motion-target.mjs path/to/target-file.tsx --root /path/to/repo
50node scripts/plan-motion-change.mjs --root /path/to/repo --target path/to/target-file.tsx --task "user request"
51```
52
53For broad skill iteration or repo-health checks, also run:
54```bash
55node scripts/check-motion-antipatterns.mjs --root /path/to/repo
56```
57
58### 2) Choose a package strategy
59Default rules:
60- **New work or modernised motion layer:** prefer the current `motion` package with imports from `motion/react`.
61- **Existing repo already on `framer-motion`:** stay consistent unless the task explicitly includes migration.
62- **Passive App Router component with no hooks or client-only logic:** `motion/react-client` can be appropriate, but it is an exception, not the default.
63- **Leaf animations with hooks, route state, presence, reorder, or interactivity:** use a small Client Component boundary.
64
65See `references/MIGRATION.md` and `references/DECISION_TREE.md`.
66
67### 3) Choose the lightest correct API
68Use this decision rule:
69- **Simple local animation:** `motion.*`
70- **Bundle-sensitive shared shell:** `m.*` with `LazyMotion`
71- **Repeated parent/child orchestration:** variants plus `stagger`
72- **Mount/unmount or route exits:** `AnimatePresence`
73- **Layout changes from React re-render:** `layout`
74- **Shared-element transition:** `layoutId`
75- **Sibling layout coordination or namespaced shared layout IDs:** `LayoutGroup`
76- **Simple scroll reveal:** `whileInView`
77- **Scroll-linked progress or parallax:** `useScroll` plus motion values
78- **Imperative sequence or external trigger:** `useAnimate`
79- **Design-system component wrapper:** `motion.create()` with ref forwarding
80
81See `references/EXPERT_PLAYBOOK.md` and `references/DECISION_TREE.md`.
82
83### 4) Wire it correctly for the router
84#### App Router
85- **Passive, hook-free animation in a server-friendly file:** consider `motion/react-client`.
86- **Interactive or hook-driven UI:** create a small Client Component leaf and keep data fetching server-side.
87- **Client wrapper around server-rendered children:** useful for modal shells, drawers, and local visibility wrappers.
88- **Route enter/exit choreography:** mount a persistent Client shell from a layout so `AnimatePresence` stays mounted.
89- **Segment replay on navigation:** `template.tsx` is useful when you want remount semantics at a specific segment boundary.
90
91See `references/APP_ROUTER.md`.
92
93#### Pages Router
94- Keep `AnimatePresence` stable in `pages/_app.tsx` for route transitions.
95- Key routed children by a stable value that changes when you actually want a transition. For dynamic routes, `router.asPath` is usually safer than `router.route`.
96- Do not rewrite `_app.tsx` for a one-off local animation.
97
98See `references/PAGES_ROUTER.md`.
99
100### 5) Apply the motion budget
101Default ranges unless the user or design system says otherwise:
102- **Micro-interactions:** 0.12s to 0.22s, scale no larger than 1.03, travel no more than 4px.
103- **Reveal / list entrance:** 0.18s to 0.35s, travel 8px to 24px.
104- **Page / route transition:** 0.22s to 0.45s, mostly opacity plus small Y translation.
105- **Layout animation:** prefer Motion springs or `layout`; do not fake these with large manual transforms.
106
107See `references/EXPERT_PLAYBOOK.md` and `references/PERFORMANCE.md`.
108
109### 6) Validate before finishing
110Always check:
111- No server/client boundary mistakes
112- Reduced motion works
113- Focus is preserved for interactive UI
114- No unnecessary layout shift or stretched content
115- No duplicate or conflicting route wrappers
116- Project still builds
117
118Run repo checks when available:
119```bash
120npm run lint
121npm run build
122```
123
124For repo audits or skill iteration, also run:
125```bash
126node scripts/check-motion-antipatterns.mjs --root /path/to/repo
127```
128
129Use `references/CHECKLIST.md` before finalising. When improving the skill itself, use `references/EVALUATION.md` and `node scripts/run-evaluation-pack.mjs`.
130
131## Implementation rules
132- Prefer animating **transform** and **opacity**. Avoid animating `top`, `left`, large filters, and large shadows on big surfaces.
133- When possible, turn the existing root element into a Motion element instead of adding a new wrapper. Extra wrappers often break layout, refs, selectors, or spacing.
134- If using `m` plus `LazyMotion`, use:
135 - `domAnimation` for standard animations, variants, exit, hover, tap, and focus.
136 - `domMax` only when you need layout animations or drag/pan.
137- Use `AnimatePresence initial={false}` for app-level wrappers unless first-load animation is explicitly desired.
138- Use `AnimatePresence mode="wait"` only when a single child should fully exit before the next enters.
139- Never key exit-sensitive children by array index.
140- Start with `layout` before manual height choreography. If content stretches, add `layout` to the affected children or switch to `layout="position"` for aspect-ratio changes.
141- If the scroll container is not the window, configure `viewport.root` or use `useInView` with the correct root.
142- When animating `next/image` or image wrappers, preserve the layout box and animate transform or opacity rather than intrinsic size.
143- When wrapping design-system components, call `motion.create()` outside render and make sure the wrapped component forwards its ref.
144- If the user did not explicitly ask for Motion and the effect is just a tiny hover or focus style on a static server-rendered element, CSS may be the cleaner answer.
145
146## Scripts
147- `scripts/audit-nextjs-motion.mjs` - inspects a repo, ranks likely target files, and emits JSON recommendations.
148- `scripts/inspect-motion-target.mjs` - inspects one file and recommends boundary, import path, risks, and likely pattern fit.
149- `scripts/plan-motion-change.mjs` - combines repo audit, target inspection, and task wording into a structured expert plan.
150- `scripts/check-motion-antipatterns.mjs` - scans a repo for common Motion and Framer Motion anti-patterns and emits JSON findings.
151- `scripts/run-evaluation-pack.mjs` - runs the bundled fixture-and-golden evaluation pack for skill iteration.
152- `scripts/scaffold-motion-primitives.mjs` - copies template components from `assets/` into a target directory, with optional import rewriting for legacy `framer-motion`.
153
154## Reference map
155- `references/EXPERT_PLAYBOOK.md` - API selection, heuristics, motion tokens, anti-patterns
156- `references/DECISION_TREE.md` - fast pattern and boundary selection
157- `references/APP_ROUTER.md` - App Router boundaries, route shells, `template.tsx`, and server-friendly patterns
158- `references/PAGES_ROUTER.md` - `_app.tsx`, keys, dynamic route nuances
159- `references/RECIPES.md` - copy/paste implementations
160- `references/PERFORMANCE.md` - bundle size, LazyMotion, layout and scroll performance
161- `references/ACCESSIBILITY.md` - reduced motion, focus, modal guidance
162- `references/MIGRATION.md` - `framer-motion` to `motion` package strategy
163- `references/TROUBLESHOOTING.md` - failure modes and fixes
164- `references/CHECKLIST.md` - final review before finishing
165- `references/EVALUATION.md` - trigger tests, scenario fixtures, anti-pattern scans, and golden-output review
166
167## Output expectations
168When modifying a repo, finish with:
1691. The files changed
1702. The Motion API and pattern chosen
1713. Boundary strategy and package migration decision, if any
1724. Reduced-motion handling
1735. Performance or bundle-size choices, if relevant
1746. Manual validation notes or commands run
1757. Any caveats the developer should know
176
177## Typical request mapping
178- "Make this button feel better" -> micro-interaction recipe
179- "Animate cards in on scroll" -> reveal recipe; variants if staggered
180- "Add a smooth route transition in App Router" -> persistent layout-mounted shell or content wrapper, not a root rewrite by default
181- "Animate this accordion or tab underline" -> `layout` / `layoutId` / `LayoutGroup`
182- "Make this drag list reorder smoothly" -> `Reorder.Group` / `Reorder.Item`
183- "This breaks in App Router" -> inspect boundary and import path before editing
184- "Modernise our Framer Motion setup" -> audit first, then use `references/MIGRATION.md`