Aspect ratio
An example of animating aspect-ratio using Motion for React's layout animations.
Source code
"use client"
import { delay } from "motion"
import * as motion from "motion/react-client"
import { useEffect, useState } from "react"
export default function Keyframes() {
const [aspectRatio, setAspectRatio] = useState(1)
const [width, setWidth] = useState(100)
const debouncedAspectRatio = useDebouncedState(aspectRatio)
const debouncedWidth = useDebouncedState(width)
return (
<div id="example">
<div className="container">
<motion.div
layout
className="box"
style={{
aspectRatio: debouncedAspectRatio,
width: debouncedWidth,
borderRadius: 20,
}}
></motion.div>
</div>
<div className="inputContainer">
<div>
<Input
value={aspectRatio}
set={(newValue) => setAspectRatio(newValue)}
min={0.1}
max={5}
step={0.1}
>
Aspect ratio
</Input>
<Input
value={width}
set={(newValue) => setWidth(newValue)}
min={10}
max={1000}
step={5}
>
Width
</Input>
</div>
</div>
<StyleSheet />
</div>
)
}
interface InputProps {
children: string
value: number
set: (newValue: number) => void
min?: number
max?: number
step?: number
}
function Input({ value, children, set, min = 0, max = 100, step }: InputProps) {
return (
<label>
<code>{children}</code>
<input
value={value}
type="range"
min={min}
max={max}
step={step}
onChange={(e) => set(parseFloat(e.target.value))}
/>
<input
type="number"
value={value}
min={min}
max={max}
onChange={(e) => set(parseFloat(e.target.value) || 0)}
/>
</label>
)
}
function useDebouncedState<T>(value: T, duration: number = 0.2): T {
const [debouncedValue, setDebouncedValue] = useState<T>(value)
useEffect(() => {
return delay(() => setDebouncedValue(value), duration)
}, [value, duration])
return debouncedValue
}
/**
* ============== Styles ================
*/
function StyleSheet() {
return (
<style>{`
.container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 300px;
height: 300px;
gap: 20px;
}
.box {
background-color: var(--hue-6);
position: relative;
z-index: 1;
}
.inputContainer {
display: flex;
flex-direction: row;
gap: 20px;
background-color: var(--layer);
padding: 20px 40px;
border-radius: 10px;
position: relative;
z-index: 2;
}
#example {
display: flex;
align-items: center;
flex-direction: column;
}
#example input {
accent-color: var(--hue-6);
font-family: "Geist Mono", monospace;
font-size: 12px;
}
#example .inputs {
display: flex;
flex-direction: column;
padding-left: 50px;
}
#example label {
display: flex;
align-items: center;
margin: 10px 0;
font-size: 12px;
}
#example label code {
width: 100px;
}
#example input[type="number"] {
border: 0;
border-bottom: 1px dotted var(--hue-6);
color: var(--hue-6);
margin-left: 10px;
background: transparent;
}
#example input[type="number"]:focus {
outline: none;
border-bottom: 2px solid var(--hue-6);
}
#example input[type="number"]::-webkit-inner-spin-button {
-webkit-appearance: none;
}
input[type='range']::-webkit-slider-runnable-track {
height: 10px;
-webkit-appearance: none;
background: var(--layer);
border: 1px solid var(--border);
border-radius: 10px;
margin-top: -1px;
}
input[type='range']::-webkit-slider-thumb {
-webkit-appearance: none;
height: 20px;
width: 20px;
border-radius: 50%;
background: var(--hue-6);
top: -4px;
position: relative;
}
`}</style>
)
}Related examples
Latest in React
Motion+
Unlock all 400+ examples
- Source code for every Plus example.
- Provide examples direct to your agent via Motion's MCP.
- Lifetime access to new examples and APIs.








