Built with Rive

Rive empty state

Maxim Vellinga

How often do you search for something specific only to be met with… nothing?
When you start a new project, you’re often looking at a blank canvas: no files, no content, everything needs to be created from scratch.
In both cases, the default experience is usually the same: a plain “No files found” message. Functional, but not exactly inspiring.
So how can we make that moment a more pleasant experience?
Rive is a perfect fit here. You can introduce a small, context appropriate animation to bring the empty state to life. For example, a playful “missing file” animation for an empty folder, or a quiet “all caught up” visual when there are no notifications left.
It’s also worth remembering: an empty state can carry different meanings. Sometimes it’s a positive signal (“no issues found”), other times it’s a negative one (“no results found”). The tone of your design should reflect that.
When you pair a thoughtful visual with clear UX writing and a well-placed call to action, you turn an empty state into a meaningful part of the experience and a great place to showcase your brand’s personality.

Technical breakdown

In this file there are two interesting elements to highlight:
The animated rings, which create a pulsing ripple effect.
Data binding, which allows switching between different states.
Animated rings
The challenge: how do I create a ping animation without duplicating content or keyframes? If you duplicate, every change means copy-pasting edits across multiple places, which is not great for maintainability.
My approach was to build a single artboard with just one animated ring. This ring has a full animation cycle: growing from small to large, fading out as it expands. I also added a second timeline with just a single keyframe of the ring completely faded out.
On the parent container, I use an artboard list to generate multiple copies of this ring. A NumWaves integer defines how many rings appear. To make sure the rings overlap smoothly, I set the gap to a negative value, which covers the full width of the ring.
Meanwhile, a looping background value runs from 0 to the highest list number, cycling every few seconds. Each ring’s state machine listens to this value and checks if it matches its own index. If it’s a match, the ring runs its ping animation.
The result: a stacked ring animation, offset in time, all driven by just one reusable ring animation.
Switching tabs
Another challenge: how can I design a single button and reuse it to switch between different designs?
Here’s how I solved it:
First, I created an artboard with the button.
Each design lives on a separate artboard as well, and they’re nested inside a Solo.
On the parent container, I set up a property group. Each Solo gets its own timeline, with a keyframe marking when it’s active.
The buttons themselves are displayed based on a list. Each list entry represents one design, with its own title.
Every button has a unique list index. On click, this index is compared against the property group, and when it matches, the correct design is displayed.
This way, one reusable button component can reliably switch between all my different designs.
Like this project

Posted Aug 14, 2025

Used Rive to turn an empty state into a dynamic experience.

Likes

0

Views

4

Timeline

Aug 1, 2025 - Aug 31, 2025

Join 50k+ companies and 1M+ independents

Contra Logo

© 2025 Contra.Work Inc