Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 0 additions & 21 deletions .claude/commands/fix-close.md

This file was deleted.

8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,13 @@ Motion adheres to [Semantic Versioning](http://semver.org/).

Undocumented APIs should be considered internal and may change without warning.

## [12.26.0] 2026-01-11
## [12.26.1] 2026-01-12

### Fixed

- Improve overload selection for `useTransform`.

## [12.26.0] 2026-01-12

### Added

Expand Down
6 changes: 3 additions & 3 deletions dev/html/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "html-env",
"private": true,
"version": "12.26.0",
"version": "12.26.1",
"type": "module",
"scripts": {
"dev": "vite",
Expand All @@ -10,8 +10,8 @@
"preview": "vite preview"
},
"dependencies": {
"framer-motion": "^12.26.0",
"motion": "^12.26.0",
"framer-motion": "^12.26.1",
"motion": "^12.26.1",
"motion-dom": "^12.24.11",
"react": "^18.3.1",
"react-dom": "^18.3.1"
Expand Down
4 changes: 2 additions & 2 deletions dev/next/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "next-env",
"private": true,
"version": "12.26.0",
"version": "12.26.1",
"type": "module",
"scripts": {
"dev": "next dev",
Expand All @@ -10,7 +10,7 @@
"build": "next build"
},
"dependencies": {
"motion": "^12.26.0",
"motion": "^12.26.1",
"next": "15.4.10",
"react": "19.0.0",
"react-dom": "19.0.0"
Expand Down
4 changes: 2 additions & 2 deletions dev/react-19/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-19-env",
"private": true,
"version": "12.26.0",
"version": "12.26.1",
"type": "module",
"scripts": {
"dev": "vite",
Expand All @@ -11,7 +11,7 @@
"preview": "vite preview"
},
"dependencies": {
"motion": "^12.26.0",
"motion": "^12.26.1",
"react": "^19.0.0",
"react-dom": "^19.0.0"
},
Expand Down
4 changes: 2 additions & 2 deletions dev/react/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-env",
"private": true,
"version": "12.26.0",
"version": "12.26.1",
"type": "module",
"scripts": {
"dev": "yarn vite",
Expand All @@ -11,7 +11,7 @@
"preview": "yarn vite preview"
},
"dependencies": {
"framer-motion": "^12.26.0",
"framer-motion": "^12.26.1",
"react": "^18.3.1",
"react-dom": "^18.3.1"
},
Expand Down
2 changes: 1 addition & 1 deletion lerna.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "12.26.0",
"version": "12.26.1",
"packages": [
"packages/*",
"dev/*"
Expand Down
2 changes: 1 addition & 1 deletion packages/framer-motion/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "framer-motion",
"version": "12.26.0",
"version": "12.26.1",
"description": "A simple and powerful JavaScript animation library",
"main": "dist/cjs/index.js",
"module": "dist/es/index.mjs",
Expand Down
29 changes: 29 additions & 0 deletions packages/framer-motion/src/value/__tests__/use-transform.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -455,4 +455,33 @@ describe("as output map", () => {

render(<Component />)
})

test("works with mixed types (string and number outputs)", async () => {
const progress = motionValue(50)

const Component = () => {
const { filter, scale, opacity } = useTransform(
progress,
[0, 100],
{
filter: ["blur(10px)", "blur(0px)"],
scale: [0.5, 1],
opacity: [0.5, 1],
}
)
return <motion.div style={{ filter, scale, opacity }} />
}

const { container } = render(<Component />)
expect(container.firstChild).toHaveStyle("filter: blur(5px)")
expect(container.firstChild).toHaveStyle("opacity: 0.75")
expect(container.firstChild).toHaveStyle("transform: scale(0.75)")

progress.set(0)

await nextFrame()
expect(container.firstChild).toHaveStyle("filter: blur(10px)")
expect(container.firstChild).toHaveStyle("opacity: 0.5")
expect(container.firstChild).toHaveStyle("transform: scale(0.5)")
})
})
76 changes: 38 additions & 38 deletions packages/framer-motion/src/value/use-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,44 @@ interface OutputMap<O> {
[key: string]: O[]
}

/**
* Create multiple `MotionValue`s that transform the output of another `MotionValue` by mapping it from one range of values into multiple output ranges.
*
* @remarks
*
* This is useful when you want to derive multiple values from a single input value.
* The keys of the output map must remain constant across renders.
*
* ```jsx
* export const MyComponent = () => {
* const x = useMotionValue(0)
* const { opacity, scale } = useTransform(x, [0, 100], {
* opacity: [0, 1],
* scale: [0.5, 1]
* })
*
* return (
* <motion.div style={{ opacity, scale, x }} />
* )
* }
* ```
*
* @param inputValue - `MotionValue`
* @param inputRange - A linear series of numbers (either all increasing or decreasing)
* @param outputMap - An object where keys map to output ranges. Each output range must be the same length as `inputRange`.
* @param options - Transform options applied to all outputs
*
* @returns An object with the same keys as `outputMap`, where each value is a `MotionValue`
*
* @public
*/
export function useTransform<T extends Record<string, any[]>>(
inputValue: MotionValue<number>,
inputRange: InputRange,
outputMap: T,
options?: TransformOptions<T[keyof T][number]>
): { [K in keyof T]: MotionValue<T[K][number]> }

/**
* Create a `MotionValue` that transforms the output of another `MotionValue` by mapping it from one range of values into another.
*
Expand Down Expand Up @@ -131,44 +169,6 @@ export function useTransform<I, O>(
): MotionValue<O>
export function useTransform<I, O>(transformer: () => O): MotionValue<O>

/**
* Create multiple `MotionValue`s that transform the output of another `MotionValue` by mapping it from one range of values into multiple output ranges.
*
* @remarks
*
* This is useful when you want to derive multiple values from a single input value.
* The keys of the output map must remain constant across renders.
*
* ```jsx
* export const MyComponent = () => {
* const x = useMotionValue(0)
* const { opacity, scale } = useTransform(x, [0, 100], {
* opacity: [0, 1],
* scale: [0.5, 1]
* })
*
* return (
* <motion.div style={{ opacity, scale, x }} />
* )
* }
* ```
*
* @param inputValue - `MotionValue`
* @param inputRange - A linear series of numbers (either all increasing or decreasing)
* @param outputMap - An object where keys map to output ranges. Each output range must be the same length as `inputRange`.
* @param options - Transform options applied to all outputs
*
* @returns An object with the same keys as `outputMap`, where each value is a `MotionValue`
*
* @public
*/
export function useTransform<K extends string, O>(
inputValue: MotionValue<number>,
inputRange: InputRange,
outputMap: { [key in K]: O[] },
options?: TransformOptions<O>
): { [key in K]: MotionValue<O> }

export function useTransform<I, O, K extends string>(
input:
| MotionValue<I>
Expand Down
4 changes: 2 additions & 2 deletions packages/motion/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "motion",
"version": "12.26.0",
"version": "12.26.1",
"description": "An animation library for JavaScript and React.",
"main": "dist/cjs/index.js",
"module": "dist/es/index.mjs",
Expand Down Expand Up @@ -76,7 +76,7 @@
"postpublish": "git push --tags"
},
"dependencies": {
"framer-motion": "^12.26.0",
"framer-motion": "^12.26.1",
"tslib": "^2.4.0"
},
"peerDependencies": {
Expand Down
16 changes: 8 additions & 8 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7420,7 +7420,7 @@ __metadata:
languageName: node
linkType: hard

"framer-motion@^12.26.0, framer-motion@workspace:packages/framer-motion":
"framer-motion@^12.26.1, framer-motion@workspace:packages/framer-motion":
version: 0.0.0-use.local
resolution: "framer-motion@workspace:packages/framer-motion"
dependencies:
Expand Down Expand Up @@ -8192,8 +8192,8 @@ __metadata:
version: 0.0.0-use.local
resolution: "html-env@workspace:dev/html"
dependencies:
framer-motion: ^12.26.0
motion: ^12.26.0
framer-motion: ^12.26.1
motion: ^12.26.1
motion-dom: ^12.24.11
react: ^18.3.1
react-dom: ^18.3.1
Expand Down Expand Up @@ -11013,11 +11013,11 @@ __metadata:
languageName: unknown
linkType: soft

"motion@^12.26.0, motion@workspace:packages/motion":
"motion@^12.26.1, motion@workspace:packages/motion":
version: 0.0.0-use.local
resolution: "motion@workspace:packages/motion"
dependencies:
framer-motion: ^12.26.0
framer-motion: ^12.26.1
tslib: ^2.4.0
peerDependencies:
"@emotion/is-prop-valid": "*"
Expand Down Expand Up @@ -11134,7 +11134,7 @@ __metadata:
version: 0.0.0-use.local
resolution: "next-env@workspace:dev/next"
dependencies:
motion: ^12.26.0
motion: ^12.26.1
next: 15.4.10
react: 19.0.0
react-dom: 19.0.0
Expand Down Expand Up @@ -12599,7 +12599,7 @@ __metadata:
"@typescript-eslint/parser": ^7.2.0
"@vitejs/plugin-react-swc": ^3.5.0
eslint-plugin-react-refresh: ^0.4.6
motion: ^12.26.0
motion: ^12.26.1
react: ^19.0.0
react-dom: ^19.0.0
vite: ^5.2.0
Expand Down Expand Up @@ -12683,7 +12683,7 @@ __metadata:
"@typescript-eslint/parser": ^7.2.0
"@vitejs/plugin-react-swc": ^3.5.0
eslint-plugin-react-refresh: ^0.4.6
framer-motion: ^12.26.0
framer-motion: ^12.26.1
react: ^18.3.1
react-dom: ^18.3.1
vite: ^5.2.0
Expand Down
Loading