Make data updates pop: Building a React component that highlights changes
- Created on
In this article, we will explore how animations can help improving UX of data/state updates.
Why it can be problematical to update values silently
Imagine you built a dashboard that displays some user data and updates the UI with fresh data (e.g., via socket connection or interval fetching).
- MRR
- $12,000.00
- Avg. MRR
- $8,000.00
- Discount
- $500.00
- Avg. Discount
- $500.00
If you look closely, the numbers change frequently! Users may struggle to notice subtle changes in the data, leading to a less engaging experience. To solve this problem, this article explores how we can use React hooks and CSS animations to draw attention to updated data points.
Highlight data updates
Of course, we don't want to highlight data that hasn't changed. So, we need to find the updated data points. How can we do this? We can compare the new and the old data:
Currently, we only keep track of our current data. If the data gets updated, we lose the previous data. So, we need to introduce something to save the previous data.
Since ref
elements persist between renders without triggering re-renders themselves, they are perfect for storing the previous value.
Here is one example implementation of a usePrevious
hook:
import { useEffect, useRef } from 'react'
export function usePrevious<T>(value: T, initialValue: T): T | undefined {
const ref = useRef<T>(initialValue)
useEffect(() => {
ref.current = value
})
return ref.current
}
With the usePrevious
hook, we can efficiently track both data
and previousData
, enabling us to detect changes. The next step is to decide how to visualize these updates effectively, ensuring they are noticeable without being distracting.
Now that we can detect changes in our data, the next step is to visualize these updates effectively. One way to do this is by applying a subtle yet noticeable animation to highlight the updated values. Something like this:
You can achieve this with a simple CSS animation:
@keyframes highlight {
0% {
opacity: 0;
}
30% {
opacity: 1;
}
100% {
opacity: 0;
transform: scale(1.05);
}
}
In general, I would go for a subtle highlight if there is only few data and a more prominent highlight if there is more or more important data. Imagine a firework once the dashboard shows $1,000,000 MRR.
Putting it together
If we put the idea of comparing the data with the previous data and add the highlight animation, we get something that looks like this:
const stats = [
{ name: 'MRR', value: 12000 },
{ name: 'Avg. MRR', value: 8000 },
{ name: 'Discount', value: 500 },
{ name: 'Avg. Discount', value: 500 },
]
export function Dashboard() {
const [data, setData] = useState(stats)
const previousData = usePrevious(data, data)
useEffect(() => {
// update the data frequently
}, [data])
return (
<div>
{data.map((stat) => {
return (
<dl key={stat.name} className="relative">
<dt>{stat.name}</dt>
<dd>
{formatCurrency(stat.value)}
{/* Compare the current and the previous data */}
{stat.value !== previousData?.[i].value && (
<div
aria-hidden="true"
className="animate-highlight absolute inset-0 ring-[1.5px] ring-blue-500"
/>
)}
</dd>
</dl>
)
})}
</div>
)
}
- MRR
- $12,000.00
- Avg. MRR
- $8,000.00
Now, one thing you might have noticed, the highlight doesn't appear if one value gets updated twice after each other. The reason is, that React wants to render the highlight for the second update but realizes that the element is already present. So, it wouldn't render it again and therefore we don't see a new animation.
So, what we want is that React renders the highlight whenever there is new data. A nice trick to enforce this is to give the animated element a unique key
prop (e.g., key={[i, stat.value].join('-')}
). This way, React will re-render (more precisely re-mount) the highlight component every time and therefore our animation works as we want.
- MRR
- $12,000.00
- Avg. MRR
- $8,000.00
- Discount
- $500.00
- Avg. Discount
- $500.00
Highlighting data updates is a simple yet effective way to improve user engagement in real-time dashboards. By combining React hooks and CSS animations, we can create dynamic and responsive user interfaces with minimal effort.