Recharts animations
Recharts supports rich animations out of the box. Animations come with default settings and are completely customizable too.
Some Recharts components are animated out of the box. Those are:
These components also support animations, but the animations are turned off by default:
Basic animation settings
All of the animated components support the same controls. Different components will have different defaults:
| Property | Type | Description |
|---|---|---|
isAnimationActive | boolean | 'auto' | Turns the animation on or off. When set to 'auto' (the default for most components), animation is enabled unless the user has requested reduced motion via their device settings. Setting true always animates, ignoring the settings. |
animationBegin | number | Delay, in milliseconds, between the first render and when the animation starts playing. |
animationDuration | number | Duration, in milliseconds, of the animation. |
animationEasing | string | function | Easing function. Can be one of the predefined string values, a custom cubic-bezier(x1, y1, x2, y2) string, or a custom easing function. More on easing below. |
onAnimationStart | function | Callback that gets called when the animation starts playing, after the first render and after animationBegin had elapsed. |
onAnimationEnd | function | Callback that gets called when the animation finishes playing. |
Animation easing
Easing is a function that controls the pace of the animation. The predefined string values are:
ease(default)ease-inease-outease-in-outlinearspring— a spring physics simulation
You can also pass a custom cubic-bezier string in the form:
cubic-bezier(x1, y1, x2, y2)
where x1, y1, x2 and y2 are numbers that define the shape of the easing curve. You can use https://cubic-bezier.com/ to visually create and understand cubic-bezier functions.
For full control, you can also pass a custom easing function directly. The function receives a progress value between 0 and 1 and returns a transformed progress value.
Accessibility
When isAnimationActive is set to 'auto' (the default), Recharts respects the prefers-reduced-motion media feature and will turn off animations for users that have requested reduced motion via their device settings. If you explicitly set isAnimationActive={true}, animations will always play regardless of this setting.
Advanced animations
On top of the basic animation props, since v3.9 Recharts also supports two new props that allow you to customize the animation completely.
Animation matching
When the data changes, Recharts needs to decide which items in the new data correspond to which items in the old data. This determines how each element animates from its old position to its new one.
By default, items are matched by their array index: the first old item animates to the first new item, the second to the second, and so on. This works great when you are replacing the entire dataset.
However, for time-series or streaming charts where new data points are appended and old points are removed, index-based matching causes every point to slide to the position of its neighbor. What you usually want instead is for each point to keep its identity and slide to its new position — or smoothly appear and disappear.
The animationMatchBy prop lets you control this behavior. This prop is available since version 3.9. It is available on all animated components:
| Value | Description |
|---|---|
matchByIndex | Match items by their array index with proportional stretching. When the old and new arrays have different lengths, old items are spread across all new positions. This is the default behavior. |
matchAppend | Match items sequentially: old item 0 pairs with new item 0, old item 1 with new item 1, and so on. Extra new items have no match and animate in from their default position. Useful when data is appended at the end. |
matchByDataKey(key) | Match items by a value in their data payload. Pass the data key name as an argument (e.g., matchByDataKey('timestamp')). Items with the same key value are treated as the same logical item and animate between positions. |
| Custom function | A function (item, index) => key that extracts a unique key from each item. Return a string or number to pair items, or null for items that should not be matched. |
Matching example: sliding window
This example shows a time-series chart with a sliding window. Use the controls to slide the window forward and compare how the two matching strategies behave:
- matchByIndex — points shift by position; the first point in the old window animates to the first point in the new window.
- matchByDataKey('time') — points keep their identity; each matched point animates to its new x-position, while new points fade in.
For timeline-style motion, this works best with type="linear" (and similarly local interpolations such as step curves). With spline-based line types such as monotone, basis, or natural, adding or removing a point also changes neighboring tangents, so the visible curve can still wiggle even when the points are matched correctly.
Our recommendation is: use linear when you want the cleanest scrolling timeline animation. If you need a smoother spline, render a few extra offscreen points on both sides and clip them so the control points that shape the visible curve remain stable.
How to clip? If you want a tighter domain you can set it explicitly using XAxis domain prop. It defaults to the extent of the data, but you can set it to a wider or narrower range to hide the points. Also note that by default, points outside of the domain are still rendered and visible! If you set allowDataOverflow= on your XAxis, then Line will render a clipPath and hide the extra points.
Matching example: stretch vs. append for data with different lengths
The matching strategy decides how old points map to new positions. When the before/after data are the same length, these two matching strategies produce the same result. When the old and new data arrays have different lengths however, different things happen:
- matchByIndex (stretch) — proportionally maps old points across all new positions. Going from 5 to 15 items, each old point "covers" roughly 3 new points, creating a stretching effect.
- matchAppend (sequential) — pairs old and new items 1:1 by index. Going from 5 to 15 items, the first 5 match directly and the remaining 10 animate in as new elements.
Swap between the 5-item and 15-item datasets below to see the difference:
Custom matching functions
For advanced use-cases, you can provide any function that returns a key from an item. The function receives the rendered item (which includes a payload property with your original data) and the item's index.
<Line
animationMatchBy={(item) => item.payload?.id}
/>Items in the old and new arrays that return the same key will animate between their positions. Items that have no match in the old data animate in as new elements, and items that have no match in the new data animate out.
Custom animation functions
The animationInterpolateFn prop lets you completely replace how items are interpolated during an animation. Instead of the default position-based transitions, you can create any visual effect: opacity fades, scale transforms, staggered entrances, or anything else.
This prop is available since version 3.9.
The function receives two arguments:
items— an array of tagged items describing what changed, ornullon the very first render (entrance animation). Each item is self-describing:{ status: 'matched', prev, next }— item exists in both datasets, interpolate betweenprevandnext{ status: 'added', next }— new item, animate in from a computed entry position{ status: 'removed', prev }— item was removed, animate out (exclude att=1)
t— a normalized time from 0 (start) to 1 (end), already eased
Return an array of items with any properties modified. The powerful trick: since each item carries both prev and next, you can return a combined array containing both — previous points fading out and new points fading in simultaneously.
Try swapping the dataset below. Each animation style crossfades between two datasets using only a small custom function — open the source code to see how they work:
Also see our other custom animation examples:
- Ranged AreaChart with custom animation
- Ranged RadarChart with custom animation
- BarChart with custom animation
Custom shape animations
While animationInterpolateFn controls how data points move during animation, some components have a built-in entrance animation that operates at the SVG level:
| Component | Default entrance animation | Description |
|---|---|---|
| Area | AreaRevealShape (clip-path reveal) | A clipPath rectangle expands from left-to-right (or top-to-bottom in vertical layout), progressively revealing the area. |
| Line | LineDrawShape (stroke-dasharray drawing) | The line is drawn progressively using strokeDasharray, creating a "pen drawing" effect. |
| All others | Data interpolation only | Bar, Scatter, Pie, Radar, RadialBar, and Funnel rely entirely on animationInterpolateFn for their entrance animation. |
To customize these entrance animations, use the shape prop. When you provide a custom shape, the built-in entrance animation (clipPath or strokeDasharray) is skipped, and your shape function receives animation state props:
| Prop | Type | Description |
|---|---|---|
t | number | Normalized animation progress, from 0 (start) to 1 (complete), already eased. |
isAnimating | boolean | Whether an animation is currently in progress. |
isEntrance | boolean | Whether the current animation is the first render (entrance) vs. a data update. |
While the shape prop has been available on several Recharts components before, the animation arguments are being passed in since version 3.9.
Built-in shapes
Recharts exports the default shapes so you can reuse or extend them in your custom animations:
import { Area, Line, AreaRevealShape, LineDrawShape } from 'recharts';
// Use the default Area shape explicitly
<Area dataKey="value" shape={AreaRevealShape} />
// Use the default Line shape explicitly
<Line dataKey="value" shape={LineDrawShape} />Custom entrance: grow from bottom
This example combines animationInterpolateFn (to move points from the chart bottom) with a custom shape (to skip the default clip-path reveal). The shape wraps AreaRevealShape but overrides isEntrance to prevent the clip-path animation:
Use the controls to toggle animationInterpolateFn and shape independently, then replay the entrance animation or swap datasets to see how they affect entrance vs. update motion.
Custom entrance: opacity fade
This Line example uses a custom shape to fade in during the entrance animation, replacing the default stroke-dasharray "drawing" effect:
Replay the entrance animation to see t and isEntrance drive the fade, then swap the dataset to compare it with a normal update animation.
Three orthogonal props
Together, Recharts provides three orthogonal props for animation customization. Each handles one concern:
| Prop | Controls | Available on |
|---|---|---|
animationInterpolateFn (v3.9) | How data points transition between positions (data level) | All animated components |
animationMatchBy (v3.9) | How old data points pair with new data points | All animated components |
shape | How the SVG element is rendered, with access to animation state (v3.9) | Area, Line (and Bar, Scatter via existing shape support) |