case_study/rhino: breaking down a mega-form into manageable chunks

Aaron Cecchini-Butler
9 min readFeb 9, 2022
The final design (the detail panel on the right)

At Rhino, we have internal tooling that allows property managers to complete many different tasks for our enterprise partners. Rhino has invested well in making sure this internal tool is built out and designed as well as many of our public-facing tools.

Recently, the responsibility for a specific set of tooling was transferred to me — and my pod and I made a number of improvements to make the tool more user-friendly, accessible, scalable (through the use of design system reusable components), and efficient.

A note about our UI

Our product involves a UI that consists of a sidenav, a main body (usually a list of cards), and a detail panel on the right (that is opened when cards are clicked). Because of certain technical constraints, it was most effective if I kept my work isolated to the detail panel.

Image of our product’s general layout, including a sidenav, body, and detail panel
Our product’s general layout

Improved UX – no more giant forms

A lot of the existing designs involve forms. We need to be able to get information from the users in order to accomplish our tasks. However, it felt as if every new iteration involved appending another section to one large form.
Although this was a great way to quickly solve the immediate problem, it became unwieldy over time — and eventually started to impact performance, as multiple calls had to be bundled together by engineering.

Our pod’s first goal was to figure out how we could break this form into smaller chunks with two main goals:

  1. Can we only ask the user for the information necessary to complete a single task at a time?
  2. Can we increase performance by separating those bundled calls?

Our first mistake

Before we had come to the conclusion that we should break down the mega-form, we spent some time designing a progress bar to keep the user informed after they had submitted the form. It could often take 10+ minutes for everything to be complete, so we thought it was important to communicate progress to avoid resubmits.

Progress bar designs
Progress bar designs
How we were going to display error states
How we (were going to) display error states

Additionally, by breaking down the steps into an approachable UI, we could also expose contextual errors — and give directions as to how to resolve those errors.

However, once we decided to break up the mega-form itself, this progress bar wasn’t needed anymore. Instead, we were able to separate the calls and expose the error states directly on our new UI.

Breaking down the mega-form

After spending some time with developers and our PM trying to wrap my head around what’s happening in the mega-form, we realized we could break it down into three chunks.

  1. The initial “Integration setup” which allows the user to run a “Resident data pull.”
  2. “Coverage rules” which allows the user to send auto-invites.
  3. “Enable policy push” which allows the user to push policy information.
Legacy and updated user flows

The Legacy user flow may seem simpler, but it had a few fatal flaws:

  1. Requires user to fill out entire form, regardless of if they are using all functionalities
  2. Required the back end to perform multiple tasks, resulting in longer loading time and increased risk of error.
  3. Required entire form to be edited when anything changed.

The updated user flow I designed allows the user to complete the bare minimum for any single task they wish to complete. This reduces the amount of time it takes for a user to accomplish specific tasks, decreases loading time, and decreases the likelihood of errors when completing any single task.

Additionally, returning to edit any specific element is simple and fast — and doesn’t require the user to swim through a large form that they are mostly leaving alone.

By keeping all of this within one form — we were forced to make a lot of calls — and it took time. Especially since the initial integration setup had to be completed before steps 2 or 3 could begin.

Breaking up the form into three smaller forms wasn’t difficult — the question was, “how do we access these three forms?”

But first, coverage rules

There was a lot of confusion surrounding coverage rules. There was the concept of both partner rulesets and custom rulesets and the original designs had a switch that allowed the user to turn on coverage rules as a whole. They were then able to add custom rulesets. However, we found out we needed the ability to turn partner and custom rulesets off/on independently.

In earlier versions, coverage rules were turned on/off as a whole
In earlier versions, coverage rules were turned on/off as a whole

We solved this by moving the switches to the individual types of coverage rules as opposed to the section as a whole.

Two separate switches for different coverage rules
Two separate switches for different coverage rules

This allows us to collapse sections when they’re turned off, resulting in a cleaner, simpler UI (and less cognitive load for the user).

Examples of collapsed sections when switch is off
Examples of collapsed sections when the switch is off

Accessing the smaller forms

The other two forms that were pulled from the mega-form were left almost entirely alone. The main challenge that remained was figuring out how the user gets to these forms.

For reasons I will get into later (check out the Scalability section of this case study) — I opted to use a table to show the multiple actions. And within the table, I included both an edit link (icon button) as well as a switch. I wanted to expose the ability to turn these options on/off directly on this page to speed up the workflow for the user — but I needed to still allow users to edit the details.

New “home” detail panel, allowing the user to turn actions on/off and access edit panels.
New “home” detail panel, allowing the user to turn actions on/off and access edit panels.

Additionally, because some of the requests that are possible can still take some time, we brought back a few little bits of that progress bar in the form of status lights within the rows.

Examples of progress indicators within table rows (as well as error state)
Examples of progress indicators within table rows (as well as error state)

And finally, it’s not possible to turn on the second two options in the table if resident data pull isn’t enabled. Because of this switch interdependence — we had to make sure that when that is off — we both disable the other switches, as well as communicate to the user how they can use those switches.
(Resident data pull is enabled by default, so this state requires the user to actively turn of resident data pull, which is unlikely.)

Disabled switches and info alert.
Disabled switches and info alert.

In order to avoid the potential initial confusion of landing on the above UI — we opted to keep our old entry point. The primary button style focuses the user’s attention on the most important action, “Set up integration.” Once the integration is set up, we bring them to the new UI with “Resident data pull” automatically turned on (to allow access to full functionality).

Accessibility and scalability

Accessibility, design system edition

One of the most effective ways to improve the accessibility of a product can be to make sure you’re using as many reusable components from the design system of your organization (assuming that you address accessibility at that level). As the owner of the design system, I know that our component library, although still young, only contains components that were designed and built with accessibility in mind.

My first pass through the existing designs involved simply updating everything to the most recent component version — as well as updating all spacing to meet our updated guidelines.
This was a rewarding way to see the impact of the design system I had been working on — and also helped solve some minor accessibility issues in how certain existing components (or one-offs) worked.


Throughout the versions I worked on — I quickly realized that we were going to be adding a LOT of functionality. Every few days I’d get another message from my copilot Pankti (an AMAZING web developer) along the lines of “hey Aaron, guess what, we’re adding something again…”

Because of these constant additions, I wanted to set up the designs in such a way that adding new functionality didn’t require us to rethink the existing UI — and this led me to the table UI that is being built now.

By using a table, adding new functionality will often only require the addition of a new table row.

In addition to actions that require user input — and allow the user to accomplish tasks — we also needed a space for actions that require the user to view information, such as changelog, or to send reports to their email.

I wanted to keep these two types of actions separate. By keeping them separate I hoped to avoid an endlessly long table of actions that weren’t likely to be related. I also wanted to start carving out a space within our UI globally for specific types of actions.

Within the existing flow, you completed everything within this form — and then were landed back on the property owner page. The only way to change information was to edit the form and resubmit (and be forced to wait 10+ minutes again). Additionally, earlier versions didn’t support enabling policy push — so this was net new functionality.

Explanation of design
Examples of how the UI scales
Examples of how the UI scales

I opted for global and passive actions to be directly underneath the header of the page. These actions should be easily accessible and I didn’t want them to be hidden below the fold. In our existing UI, two of the global functions (Edit and Delete) were part of a two-button FAB that has proven repeatedly to be problematic for users, as well as a functional burden for engineers. My hypothesis is that moving both of those global functions up into this action button group will increase the speed in which users can find these actions.

I opted for table rows for more task-based actions because of the flexibility built into our table rows. Although our current actions are all switches (and an edit button per row) — there is always the potential for future functionality to require other means of interaction. A table row can support most means of interaction within our system.

Some examples of potential future table rows
Some examples of potential future table rows


As we build out this feature, I am working with our engineers to try and measure how much more efficient the new design is. By breaking up the mega-form, we allow the users to complete the minimum amount of actions before submitting anything. This allows us to make fewer calls per UI. Additionally, there will be many times when not all three switches are turned on – in these cases, we’re always saving the user time by not forcing them to both fill out the form regardless, as well as not making extraneous calls.


The design decisions we made for this feature reduced the amount of time necessary to accomplish core tasks by more than 50%.

This translates to money saved for the business and headaches saved for the users.

When we demoed this to one of the primary users — he said, “Cool, no questions. This is going to save a lot of time.”
There’s no greater compliment in my opinion.



Aaron Cecchini-Butler

Senior Systems Designer at Grubhub working on Cookbook (our design system) — as well as contributing to product design work.