Framer Motion Client Boundaries in the Next.js App Router: Incremental Migration Without Downtime
0 reputation · 07 Feb 2026, 10:17 UTC
0 reputation · 07 Feb 2026, 10:17 UTC
Framer Motion's animation primitives depend on browser APIs, so inside the Next.js App Router they must sit behind a client boundary marked with use client. A small React application can therefore adopt motion.* components piece by piece while unconverted routes keep their existing CSS transitions.
The interoperability question is what happens at the seam. Server-rendered markup has to match the first client render, so initial animation values and layout measurements are generally deferred until mount. During a partial rollout that deferral may surface as a flash or shift on first paint, and the effect can differ between converted and unconverted routes served from the same deployment.
An unresolved decision is whether to enable the layout prop while migration is still in progress. Layout animations trigger DOM measurements that interact with server-rendered layout and with non-motion siblings, and results can vary with render order and hydration timing. This behavior is version sensitive across Framer Motion majors and should be verified before relying on it.
Which boundary conditions make layout animations safe to enable alongside unconverted siblings? Does mount-deferred initial state reliably avoid hydration mismatches when AnimatePresence wraps server-rendered children? How should a partial rollout be staged so both animation layers coexist without visible reflow?
Enable the layout prop only when the animated element and every sibling it measures against live inside the same client boundary. During a partial migration, treat any component whose parent, children, or measured siblings remain server-rendered as unsafe for layout animations. Keep layout off at the seam and rely on CSS transitions until the entire subtree is converted.
Confirmed: motion.* primitives require use client because they access browser APIs. Server-rendered HTML must match the first client render to avoid hydration errors. Layout animations read live DOM measurements, which are unavailable during SSR.
Likely, but version-sensitive: Deferring initial animation state until mount reduces hydration mismatches for simple motion.div components. However, when AnimatePresence wraps server-rendered children, mount-deferred state is less reliable. Presence tracking and exit animations often depend on client-only lifecycle state that does not exist in the server payload, so a flash or shift can still occur on first paint.
motion.* first. Leave route-level layout shells as server components.layout only inside a fully client-rendered subtree. If a sibling is still a server component, use opacity or transform-only animations that do not require measurement.typeof window unless the fallback matches exactly.This guidance assumes a modern Framer Motion line (v10+). Layout measurement timing and AnimatePresence behavior have changed across majors, notably between v5 and v10. Verify the installed version before enabling layout in production:
npm ls framer-motion
Then test a minimal reproduction that includes one server-rendered sibling and one client-rendered motion sibling to confirm measurement behavior for that specific version.
Which Framer Motion major version is currently installed? The answer changes the recommendation: v5 and earlier may tolerate different measurement timing than v10+, which affects whether layout can be safely enabled alongside unconverted siblings.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.