---
title: Rendering Lists
description: How to efficiently render lists of items from store state.
---
## The pattern
Rendering a list of items from store state follows a three-step pattern:
1. **Subscribe to the list length** with `useAuiState` — re-renders only when items are added or removed
2. **Memoize the element array** with `useMemo` — avoids recreating elements on unrelated re-renders
3. **Use `RenderChildrenWithAccessor`** inside each item — defers reading item state until the child actually needs it
```tsx
import { useMemo } from "react";
import { useAuiState, RenderChildrenWithAccessor } from "@assistant-ui/store";
const TodoList = ({
children,
}: {
children: (value: { todo: TodoState }) => ReactNode;
}) => {
const length = useAuiState((s) => s.todoList.todos.length);
return useMemo(
() =>
Array.from({ length }, (_, index) => (
aui.todoList().todo({ index }).getState()}
>
{(getItem) =>
children({
get todo() {
return getItem();
},
})
}
)),
[length, children],
);
};
```
Usage:
```tsx
{({ todo }) => }
```
## Why this pattern?
### Length-based subscription
Subscribing to `.length` instead of the full array means the list component only re-renders when items are added or removed — not when an individual item's data changes. This is significantly cheaper than subscribing to keys or the full array.
### RenderChildrenWithAccessor
`RenderChildrenWithAccessor` provides a lazy accessor (`getItem`) instead of reading item state eagerly. This means:
- **Deferred reads**: If the children render function never accesses the item, no subscription is created
- **Propless memoization**: When children returns a propless component (e.g. `{() => }`), the output is automatically memoized — it won't re-render on parent updates
The `getItem` function is a getter that reads the latest state on demand. Wrap it in a lazy property (`get todo() { return getItem(); }`) so the state is only read when the consumer accesses it.
## Built-in primitives
All assistant-ui list primitives (Messages, Parts, Attachments, Suggestions, ThreadListItems, etc.) use this pattern internally. When using the children render function API, you get the lazy accessor behavior automatically:
```tsx
{({ message }) => {
// `message` is a lazy getter — state is read here, not above
if (message.role === "user") return ;
return ;
}}
```