Five Techniques for Achieving Fluid-UI on the Web

Ben McMahen
Frame.io Engineering
5 min readJan 31, 2020

On Frame.io we recently introduced the MoveTo/CopyTo feature which allows users to more easily move assets between folders, projects, and teams. When building UI, we always have the goal of providing a fluid, responsive interface which loads content seamlessly and responds immediately to user input. In this article, I want to explore five techniques that I used to create a Fluid-UI experience on the web.

Move-To/Copy-To in action.

What makes a user interface fluid?

An interface feels fluid when it feels like an extension of your mind — when it feels natural or part of the real world — and when the layers of abstraction between the user and interface disappear. This is something that Apple explains in their WWDC 2018 talk, and it’s partly what makes iOS so easy and fun to use. And while much of Apple’s talk centered around Fluid-UI within the context of native touch based interfaces, many of their lessons can still be applied when developing a more traditional, mouse-based interface on the web.

Animations should be responsive and interruptible

Typically on the web we’ve defined animations using a fixed duration and curve, like this:

button {
transition: width 0.3s ease;
}

But as Apple’s ex-UI-Kit developer Andy Matuschak explains, this way of defining animation is fundamentally opposed to a Fluid-UI.

Consider a drag and drop interface. When an entity is dropped, you’ll want to animate that entity to its new position. When that animation duration is fixed in duration and curve, the same animation will play no matter where the entity is dropped or with what velocity. Instead, the animation should vary with each drop — it should vary depending on the direction of the drop, infer its initial direction, the distance it needs to cover, and so on. And this gets me to my first point:

1. Use spring based animations (sometimes).

Using a spring configuration animation, such as that provided by framer-motion or react-spring, allows you to use physics based animations which act naturally no matter the location of the drop.

You’ll want to reserve spring based animations for interactive, highly variable animations that can be cancelled. When you do use springs in this context, you’ll find that you can much more easily achieve a natural feeling result.

Take this example from Mihai Cernusca built with react-spring:

Press the relevant keys in rapid succession, and you’ll notice that the interface responds immediately and naturally.

Here’s another more complex example from Paul Henschel, the creator of react-spring.

As Paul explains:

2. Use :active states for interactive elements

To achieve a fluid user interface, the interface should respond immediately to user interaction. Low latency (a UI which responds visually to interaction in 50ms or less) is key. One of the easiest ways to achieve this on the web is to ensure interactive elements maintain an :active visual state. For MoveTo, we use a combination of :hover, :active, and selected states which makes the tree elements feel interactive and responsive.

Each clickable element has an :active state

3. Preload data when the user shows intent.

In MoveTo, the ultimate goal is to provide an experience which has no obvious loading states. We fetch as little information as possible, and only load project and folder lists when the user signals their intent to view them. We do this by fetching data from the API in our onMouseDown event handler. This is a technique suggested by Eli Fitch, and more recently by Facebook engineer Joseph Savona in the context of Relay and React’s upcoming concurrent mode — but it’s a technique you can start using today.

We at Frame.io make use of redux and redux-saga, and have the ability to dispatch a fetch action in our onMouseDown event handler.

Is this micro-optimization actually worth it? As Eli Fitch explains,

triggering a fetch in onMouseDown typically gives you a 100–150ms head start.

And when the interactive element has an :active state, that number is typically closer to 200ms (Yes, for some reason users engage the click longer when it has an active state!). For most requests, this will be enough time to instantly render the lazy loaded data as soon as the click has occurred.

4. Don’t show loading spinners unless necessary.

Sometimes a request will take more than 200ms. The temptation here is to immediately render a loading spinner, but you really only need to render a loading spinner when a request takes over one second. Eli Fitch and Guillermo Rauch both explain that loading spinners actually make a request seem longer — it tells the user that they have to wait for something, and puts them in a passive state. Instead, you should defer rendering of a spinner until necessary, often not until 1s has passed (but it’s a number worth experimenting with).

In MoveTo, we subtly fade in an inline-spinner after 400ms to indicate to the user that their request is currently being made. Notably, the user interface still responds immediately to the click — but the loading indicator itself is saved for only the very slowest requests.

A spinner displays inline for slower requests.

5. Defer mounting of expensive renders to avoid jank

Sometimes when working in React, you’ll experience an obvious delay when a click event triggers an expensive render. We were running into this issue when initially opening the MoveTo modal dialog when rendering a large initial tree. The modal animation itself was delayed until the entire tree was rendered, which could sometimes takes roughly 200–300ms.

The solution here is to delay the rendering of the tree until the browser has successfully animated the modal itself, using a technique outlined by twitter. We’ve adapted this approach in the form of a hook:

What’s interesting here is that, in terms of time to render, our solution is actually slower. But it feels faster because the interface responds immediately to user input. The ideal solution here is to optimize the expensive renders themselves (or to wait for React’s concurrent mode), but this solution combined with a simple fade animation makes the interface feel seamless.

A path to Fluid-UI on the web

These are a few of the techniques we’ve used in our new MoveTo feature to achieve Fluid-UI. The key, ultimately, is a combination of smart engineering combined with techniques to improve perceived performance, emphasizing low latency, fluid and natural interactions. What techniques have you used to improve the fluidity of your web application? What are some examples of websites that you feel embody Fluid-UI principles?

--

--

Ben McMahen
Frame.io Engineering

A product developer and designer based in beautiful British Columbia, Canada. Passionate about eduction, technology, and peanut butter.