#3801·motion

[BUG] @types/react is not declared as a peer dependency, breaking types under strict linkers

Author: tifandotmeCreated Aug 30, 2026Updated Aug 30, 2026

2. Describe the bug

motion and framer-motion declare react and react-dom as optional peer dependencies, but not @types/react. Their type files import React:

typescript
import * as React from "react"

Under a strict, non-hoisted node_modules, those type files cannot resolve React's types. Every motion component's props degrade and TypeScript reports:

error TS2607: JSX element class does not support attributes because it does not have a 'props' property

3. IMPORTANT: Provide a CodeSandbox reproduction of the bug

Not possible here, and I want to be upfront about that rather than link something that does not show the problem. CodeSandbox installs with a hoisted node_modules, which is exactly the layout that hides this bug. It only appears under a strict linker: Bun isolated, pnpm, or Yarn PnP.

The local reproduction is four files:

json
// package.json
{
  "devDependencies": {
    "motion": "13.1.1",
    "react": "^19",
    "react-dom": "^19",
    "@types/react": "^19",
    "typescript": "^5"
  }
}
toml
# bunfig.toml
[install]
linker = "isolated"
json
// tsconfig.json
{ "compilerOptions": { "jsx": "react-jsx", "strict": true, "noEmit": true, "moduleResolution": "bundler", "module": "esnext" } }
typescript
// a.tsx
import { motion } from "motion/react"
export const A = () => <motion.div className="x" />

4. Steps to reproduce

  1. bun install
  2. npx tsc -p .

5. Expected behavior

Types resolve and the file compiles.

7. Environment details

macOS 26.5, Bun 1.4.0, React 19.2, TypeScript 5.9. Also reproduces with pnpm.


Why it happens

A strict linker symlinks each package into a store. Node and TypeScript resolve a symlink to its real path before walking parent directories, so a package sees only what it declares. react is declared and resolves; @types/react is not, so it does not.

skipLibCheck suppresses the errors inside motion's own .d.ts files, but not the broken types they hand to consumers.

Suggested fix

Add @types/react alongside the existing optional peers in packages/motion and packages/framer-motion:

json
"peerDependencies": {
  "@types/react": "*"
},
"peerDependenciesMeta": {
  "@types/react": { "optional": true }
}

Same pattern @radix-ui/* and [email protected] already ship. Happy to open the PR.