Creating a Table variant for our UI Kit in Figma (Case Study)

Carol Chan
Sep 3 · 8 min read

Our team has been working through a list of Jira tickets to convert some components into variants and, admittedly, we left the hardest ones for last. A particularly challenging set are our ❖DataTable and ❖DataGrid components which come with a lot of different configuration options. They are both “tables” with the only difference being that one has a flexible cell height, and the other has a fixed cell height.

Old DataGrid components for bottom border only, zebra, and all borders
Row-based component to allow for flexible row heights needed in DataTable


While our current table component was thoughtfully considered, after being in use for a while, we identified some downfalls:

  1. It required a video explanation which designers didn’t have time for. Although we had good intentions, designers often opted to figure out the component themselves and detach things to make it work.
  2. Performance issues — We shipped the entire table as a component which meant that designers needed to turn on/off a bunch of layers. Thus, when engineers and other stakeholders tried to load prototypes/files with many tables, the hidden layers bloated the file and made everything extremely slow to load. You can read more on reducing memory usage in files.
  3. Not transparent — Although components were built and organized well and could be used through the instance swapper, designers didn’t have full knowledge or visibility of the options available.


Reducing complexity

With Figma releasing Variants as a feature, it gave us an opportunity to revisit the ❖DataTable and ❖DataGrid components to see if we could make things simpler. The ability for variants to have properties that can be used to swap between different styles and states allowed us to surface the options available in our component.

Comparison between instance swapper vs. variants properties panel

As I laid out potential options for how to structure the variant, it was useful to think about things from an atomic design approach.

The potential variants needed to make up a table included ones for:

  • Each cell in a table (normal cells and in-line edit cells)
  • Each column header in a table
  • Each row or column in a table initially restricted to one data type, but configurable in layers
  • … and then packaging these atomic units into a “table

I tried to map out what properties these variants might need and which level to surface them. When things got too complex, I thought about how we can simplify the solution. There were also some one-off use cases that I decided to leave out of the variant, but could be accommodated with an empty cell.

Some of the variant numbers were getting a bit crazy when I tried to place too many properties on the top level. So, I wanted to do more investigation to see how much complexity designers could handle on their own.

Testing with the team

In order to avoid falling into the same trap of relying on assumptions to create a component — producing something that isn’t understood by designers other than the one who made it — I decided to do a UX research activity during our UX studio time. Even though UI kit components aren’t for a paying customer, it doesn’t mean we can’t benefit from using product design practices 😊!

I gave designers 3 different types of cells as variants (a normal cell, in-line edit cell, and column header) and asked them to replicate tables using only those pieces and their knowledge of Figma.

The idea was to figure out if designers could configure the smallest pieces of a table first, observe how they were being combined and use these insights to inform how I should combine them in our variant, and how many nested layers are required.

Three cell types given in the activity

For the activity, designers were given a choice from 3 tables in-app (A, B or C) to replicate. It was interesting to observe that some designers used groups, some used frames, and others used autolayout to build their tables cell by cell.

Table A requires designers to understand how to use the instance swapper
Table B requires designers to explore different column types and borders
Table C has elements not configured in the variant to test how designers use workarounds

After the activity, I asked the team about their experience. Although the cells themselves were straightforward to use, the team agreed that it was taking too long to make a table from scratch. One designer remarked that they would probably make a table out of these pieces and keep using that one in their own files — So making one for everyone would be best.


The final component ended up being a variant that could address the fixed and flexible height scenarios designers need for DataGrid and DataTable. We named it “❖Table” for brevity and noted its names (DataGrid and DataTable) from our react component library as part of the description.

The idea was to have certain variants configured as containers to “ship” necessary elements to designers. It’s like thinking about how to package socks for sale — Do you want 3-packs, 6-packs or 12-packs? Stripes, solids or polka dots? Once these container variants have served its purpose, they can be detached.

The structure of ❖Table and its nested variants are diagrammed below. The grey containers are meant to be detached, while blue items are not.

When designers pull a ❖Table from the UI kit, it comes with 3 columns and embedded instructions. They can also adjust the ❖Table’s action bars, scrollbars, and footers. The variant’s description give designers an overview of what they can find nested and if detaching is recommended.

Keeping the columns as variants allows designers to quickly swap out between types of columns and adjust things in bulk like changing the background to zebra striping, or adjusting borders.

Once more columns need to be added though, the variant needs to be detached at the ❖Table level to allow duplication of the ❖DataColumn (copy, paste and configure). Designers can then delete the guidance note.

Selected columns all have the same attributes and can be changed in bulk

Another benefit of having columns as variants is being able to quickly adjust the columns to zebra or adjust the rows of all columns by multi-selecting. The columns were created with a row property that allows for 1–16 rows. The 16 row max covers the 1280px by 800px screens we typically design for at Galvanize.

Adjusting selected columns to zebra
Adjusting selected columns to have 12 rows — Reveals turquoise background

The background of Table is also set to a vibrant turquoise to signal to designers that they need to fix something in their design.

If designers need to adjust the row heights in their table, ❖GridColumn can then be detached. The structure of the table remains, but the designer loses their ability to configure in bulk. This is still easy to do by selecting all ❖DataCells in a column — the properties were simply surfaced in bulk on the ❖GridColumn level.

I also considered allowing designers to configure tables by rows and then copying and pasting each row as needed, but it required more fine-tuning than the column method and didn’t allow for bulk changes to occur without lots of multi-selecting and targeting each cell.

That being said though, designers also have the option to target each cell to switch them to in-line edit mode (❖DataCell/In-line Edit) through the instance swapper panel.

More advanced structure with both ❖DataCell/Default and ❖DataCell/In-line Edit

Designers can also target the column headers and customize icons, sort order, and the column type.

❖Table can be switched to have borders configured as a grid or rows on the column level, or cell by cell.

To address the problem of hidden layers, I made sure to add ❖DataCells in ❖GridColumn only as they were needed instead of hiding the layers. Both examples below achieve the same result, but the top example does not have any hidden layers and therefore better performance.

More Testing

I shared the component back to the team to play with for additional feedback and ran through the table activity we did before with the new variant.

First round of testing with smallest table units vs. second round with a full table variant

The team helped clarify some of the terminology I was using and caught things I missed like sorting properties by alphabetical order in all variants. There were also no visual differences between icon cells and button cells except when you hover over a button in a prototype through interactive components, so a note was added to make designers aware of the two types.

Finally, we pulled some mocks from current projects to apply the table variant to as a final check 😄

Key Takeaways

  • Finding in-app examples to test your component with is a great way to ensure you’ve considered edge cases in your solution.
  • Design system teams can benefit from treating other designers as their “customers” to conduct user research or co-create with them to make better components.
  • Not all variants need to be used as ‘components’. Some can serve as containers to package components together for the end user before they are detached.


You can find a living version of this component in our Paprika component library or… if you’d like to dig into the UI kit implementation further and see a copy of the table activity, you can duplicate this community file.

Build Galvanize

A window to the product, design, and engineering teams at…