Summary
<Accordion> from @openuidev/react-ui opens the last item on initial mount, even for a fully static (non-streaming) response. Expected is the first item open (as the pre-refactor implementation did), or all items closed.
This is the same defect shape as the <Tabs> issue #819: a streaming "follow the newest content" heuristic that doesn't exclude the initial mount.
Reproduction
Standalone repro (published packages only, npm install && npm run dev):
https://github.com/Shinyaigeek/openui-accordion-repro
root = Accordion([i1, i2, i3])
i1 = AccordionItem("a", "Apple", [c1])
i2 = AccordionItem("b", "Banana", [c2])
i3 = AccordionItem("c", "Cherry", [c3])
c1 = TextContent("Apple section content")
c2 = TextContent("Banana section content")
c3 = TextContent("Cherry section content")
- Expected: the first section (
Apple) is open on mount, or all sections start closed.
- Actual: the last section (
Cherry) is open on mount.

Versions: @openuidev/react-ui@0.13.1, @openuidev/react-lang@0.2.9, @openuidev/react-headless@0.9.3, React 19.
Root cause
In packages/react-ui/src/genui-lib/Accordion/index.tsx, the auto-open logic runs during render and treats any growth in item count as "a new item arrived during streaming":
const [openItem, setOpenItem] = React.useState<string>("");
const userHasInteracted = React.useRef(false);
const prevItemCount = React.useRef(0);
// Auto-open: only when a NEW item arrives during streaming
if (!userHasInteracted.current && items.length > prevItemCount.current) {
const newest = items[items.length - 1];
if (newest) setOpenItem(newest.props.value);
}
prevItemCount.current = items.length;
On the very first render prevItemCount.current is 0, so a fully static N-item accordion satisfies items.length > 0 and the branch opens items[items.length - 1] — the last section. The baseline pass is indistinguishable from streaming growth.
The earlier implementation had a mount effect that opened the first item; the refactor in #485 removed it, leaving only the auto-open-newest branch.
Related streaming latch in the same branch
While actually streaming there is a second problem: when a new item appears before its value string has finished streaming, setOpenItem latches a prefix of the value (e.g. "ov" for "overview"). The only re-sync trigger is item-count growth, so the completed value never re-syncs; the latched prefix matches no item and the accordion ends up with no section open until the user clicks. (Items are not filtered for complete values — contrast Tabs, which filters value != null.)
Suggested fix
Mirror the Tabs fix (c2b1e7e for #819):
- Skip the auto-open pass on the baseline render (e.g. seed
prevItemCount from the first non-empty items.length), so a static mount opens the first item (or none) instead of the last.
- Only auto-open items whose
value is complete, and re-sync when a previously-partial value completes, so the streaming auto-open doesn't latch a prefix.
Summary
<Accordion>from@openuidev/react-uiopens the last item on initial mount, even for a fully static (non-streaming) response. Expected is the first item open (as the pre-refactor implementation did), or all items closed.This is the same defect shape as the
<Tabs>issue #819: a streaming "follow the newest content" heuristic that doesn't exclude the initial mount.Reproduction
Standalone repro (published packages only,
npm install && npm run dev):https://github.com/Shinyaigeek/openui-accordion-repro
Apple) is open on mount, or all sections start closed.Cherry) is open on mount.Versions:
@openuidev/react-ui@0.13.1,@openuidev/react-lang@0.2.9,@openuidev/react-headless@0.9.3, React 19.Root cause
In
packages/react-ui/src/genui-lib/Accordion/index.tsx, the auto-open logic runs during render and treats any growth in item count as "a new item arrived during streaming":On the very first render
prevItemCount.currentis0, so a fully static N-item accordion satisfiesitems.length > 0and the branch opensitems[items.length - 1]— the last section. The baseline pass is indistinguishable from streaming growth.The earlier implementation had a mount effect that opened the first item; the refactor in #485 removed it, leaving only the auto-open-newest branch.
Related streaming latch in the same branch
While actually streaming there is a second problem: when a new item appears before its
valuestring has finished streaming,setOpenItemlatches a prefix of the value (e.g."ov"for"overview"). The only re-sync trigger is item-count growth, so the completed value never re-syncs; the latched prefix matches no item and the accordion ends up with no section open until the user clicks. (Items are not filtered for complete values — contrastTabs, which filtersvalue != null.)Suggested fix
Mirror the
Tabsfix (c2b1e7e for #819):prevItemCountfrom the first non-emptyitems.length), so a static mount opens the first item (or none) instead of the last.valueis complete, and re-sync when a previously-partialvaluecompletes, so the streaming auto-open doesn't latch a prefix.