What are suggested component+data source setups for showing nested arrays?

tl;dr:
Retool seems to work good OOTB for presenting requests that return [{key:value}] or [{key:{key: value}}]
but
Whenever request contains [{key:value, additional_objects:[{key: value},{key: value}]}] it either requires haxes or significantly degrades UX.

Hi!
In the past I’ve made Retool apps that only ever handled back-end responses containing arrays with nested objects that at worst had relation of 1:1 (each row/array’s object had exactly one child object) so displaying, styling and editing it wasn’t ever an issue due to me always being able to reference parent table (via currentRow/selectedRow, self or item) for everything. Aside from a known Retool bug on styling hidden vs disabled due to different contexts :salt:)

My usual flow was:

  • parent table, with togglable rows
  • child tables (inside of tabbed component,) for (every) nested object (using {{currentSourceRow.subarrayKey}})

However recently I needed to create apps that aimed to showcase more complex relations where each row contained an array of related objects and setup of:

  • parent table, with togglable rows
  • child tables (inside of tabbed component,) for (every) nested array of objects

and above setup doesn’t seem to cooperate well with Retool table component assumptions.

Examples:

1. 1:1 relation
(here everything works, since you can easily reference parent table and just drill down to a specific key-value)

BE response pattern:

[{"id": 1,"credentials": {"id": 111,"inserted_at": "2025-08-05T13:38:34",},"inserted_at": "2025-02-13T13:54:38","workspace_id": 6720,"project": {"id": 2119,"location": "United States","inserted_at": "2025-01-07T12:27:52"},"map": {"id": 8858,"state": "done","inserted_at": "2026-02-18T16:37:16"},"last_edited_at": "2026-02-18"},...]

Things I need:

  • Button components outside of child table that trigger per row actions (ex. getting map’s id for request using {{tableName.selectedRow.map.id}})
  • styling of parent rows based on child table rows

Things that work:

tl;dr - here all works smoothly. :smiley:

  • Button components outside of child table that trigger per row actions (ex. getting map’s id for request using {{tableName.selectedRow.map.id}})
  • styling of parent rows based on child table rows (using references like {{currentRow.project.location}} for row color)

2. 1:n relation with specific UX/styling requirements
(here’s where most of things aside from listing breaks, since using {{currentRow.arrayName}} as data source still makes table work in context of entire parent table instead of just currentRow data)

BE response pattern:

[{"members": [{"active": true,"inserted_at": "2023-07-03T11:33:11","email": "test1@gmail.com","role": "owner"},{"active": true,"inserted_at": "2023-11-06T14:33:15","email": "test2@gmail.com","role": "member"},...],"organization_id": 16202,"workspaces": [{"id": 11765,"name": null,"inserted_at": "2023-10-16T15:07:50"},{"id": 53175,"name": "Say my name","inserted_at": "2026-01-12T11:13:26"}],"organization_name": null},{"members": [{"active": true,"inserted_at": "2023-10-25T13:41:30","email": "test+20@gmail.com","role": "owner",},{"active": false,"inserted_at": "2024-08-05T14:09:24","email": "test1@gmail.com","role": "member",},...],"organization_id": 16658,"workspaces": [{"id": 12217,"name": null,"inserted_at": "2023-10-25T13:41:30"}],"organization_name": null},...]

Things I need:

  • multiselect on child table
  • styling of parent rows based on child table rows
  • styling of child rows based on child rows
  • styling custom column Buttons of child rows based on child row data
  • not running custom column Buttons event handlers depending on child row data
  • running per row actions
  • styling Button components next to child table based on child table row data

Things that don’t work:

  • multiselect on child table and batch actions always points to the same table (when referencing {{childTable.selectedRows}} will always point to one specific child table or be undefined depending on the exact place you place the reference, regardless of the true selected row/rows origin)
  • styling of child rows based on child rows (as despite child table having it’s own data source, styles are computed based on parent table’s complete array)
  • styling custom column Buttons of child rows based on child row data (again child having parent table’s complete array)
  • styling Button components next to child table based on child table row data (impossible to get this context?)

Things that work:

  • not running custom column Buttons event handlers depending on child row data (different available context that’s more local?)
  • running per row actions has same issues as multiselect, but can be haxed around using intermediary variables (so instead of request relying on table.data.key directly, I have a custom row button in child’s table, that grabs {{ currentRow.id }} via Event handler, saves it to app-wide variable, and then request uses {{ appWideVarName.value }}
  • styling of parent rows based on child table rows works only as a .filter/.map hax if you have within parent and child same exact data point to match it within JS snippets :confused:

3. 1:n relation with full CRUD requirement, including moving child objects between parents (which again breaks whenever you want to use 2 components for displaying purposes)

BE response pattern:

[{"id": 291,"name": "optimization","inserted_at": "2025-12-18T13:12:20.832Z""prompts": [{"id": 1293,"text": "What questions do you ask AI?","inserted_at": "2025-12-18T13:12:20.832Z"},...]},{"id": 299,"name": "apple","inserted_at": "2025-12-18T13:12:20.832Z""prompts": [{"id": 1299,"text": "Why Macbooks have stupid NAND memory integration designs?","inserted_at": "2025-12-18T13:12:20.832Z"},...]},...]

Things I need:

  • adding a new object to parent table/array (including cases where rows can have incomplete kvs)
  • adding a new object to child table/array (including cases where rows can have incomplete kvs)
  • editing values on parent table
  • editing values on child table
  • moving objects between child tables/arrays
  • deleting objects in child table
  • deleting objects in parent table

Things that don’t work:

(to attempt full CRUD without multiple different UX actions and multiple saves I attempted to make a app-variable that would have working copy of data I modify with UI elements, instead of using request.data and Retool’s built-in change sets)

  • adding a new object to parent table/array (not compatible with how Retool handles select editable columns, as appending new data to variable that’s table source triggers edit mode automatically?)
  • adding a new object to child table/array (-||-)
  • moving objects between child tables/arrays (not supported?)
  • deleting objects in child table (impossible if attempted within same move as add?)
  • deleting objects in parent table (-||-)

Things that work:

  • flattening BE output into cartesian object and then redoing object layout back up before sending update POST request (*maybe would work, but is very error prone in my PoC attempts)
  • editable JSON field (but that heavily depends on users requiring JSON editing knowledge and providing logic and syntax-wise a correct code on every edit :confused: )

I’ve been attempting to gracefully solve both 1:n nested arrays cases and I’m kinda lost, hopefully I’m missing something obvious - thank you in advance for any insights/suggestions. :folded_hands:

Cheers!

Hi @Piotr_Zubko,

Thank you for the well articulated post!

The root of most of your pain is that Retool's table component was designed for flat data. For 1:n cases, the most reliable mental model is to

Treat Retool as a thin UI shell, and keep your data logic entirely in transformers and JS variables, not in component references.

The more you lean on currentRow / selectedRow for nested structures, the more you'll hit Retool's context resolution bugs. A clean workingCopy variable + transformers that derive display slices from it is the most maintainable pattern for your use cases.

For Case 2, this is a known Retool quirk. The workaround is to use a separate query/transformer that isolates the child data before it ever reaches the table.

Unfortunately there is not 'out-of-the-box' handling for nested data structures, but the good news is that using JS queries and transformers, you can coerce the data into shapes that are better supported by the default components.

With custom components as a back up option where you need complex parent to child component interactions.

1 Like