Accordion opens the last item on initial mount instead of the first
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Newbie friendliness
- 25/100
- Issue type
- Bug
- Clarity
- Clearly specified
- Activity status
- Stale
- Tech stack
- react, typescript
- Domain
- frontend
Research direction
Start in packages/react-ui/src/genui-lib/Accordion/index.tsx and compare the auto-open behavior with the Tabs fix for issue #819. Check for existing Accordion tests or add coverage for a static mount and a streaming value that completes after its item appears. Done means initial mount does not open the last item, and streaming does not leave a partial value selected.
Written by the indexing model from the issue text.
Description
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/[email protected], @openuidev/[email protected], @openuidev/[email protected], 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 (c2b1e7ef for #819):
- Skip the auto-open pass on the baseline render (e.g. seed
prevItemCountfrom the first non-emptyitems.length), so a static mount opens the first item (or none) instead of the last. - Only auto-open items whose
valueis complete, and re-sync when a previously-partialvaluecompletes, so the streaming auto-open doesn't latch a prefix.
- Dominant language
- TypeScript
- Stars
- 10k
- Forks
- 698
- Avg merge
- 1d 6h
- Merged PRs (30d)
- 148
Getting set up
This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from thesysdev/openui
-
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 70/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
thesysdev/openui#1445 · 2 comments ·
Maintainers usually reply within 1 day
-
Add dataInvention flag to PromptOptions to disable the 'generate realistic/plausible data' ruleOpen
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
thesysdev/openui#959 · 2 comments ·
Maintainers usually reply within 1 day
-
Difficulty 5/5 Over a week Newbie friendliness 18/100
Maintainers usually reply within 1 day
All issues in thesysdev/openui
Similar issues
-
Billion-context documentation addendum: nudge is independent of window occupancy. | billion-context 文档补充:nudge 与窗口占用无关Possibly taken A pull request linked to this issue is open or already merged. Open
Difficulty 1/5 1-3 hours Newbie friendliness 86/100
ranxianglei/billion-context#5893 ·
Maintainers usually reply within 1 day
-
stringify escapes leading digits in pseudo-class arguments (:nth-child(1) becomes :nth-child(\31 ))Open
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
Maintainers usually reply within 1 day
-
enhancement security severity:low
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
Battle-Cattle/BCUK-Bot-4#959 ·
Maintainers usually reply within 1 day
-
[Bug]: Claude account rows no longer show the email (since #8055), so "Account 1, 2, 3" cannot be told apartPossibly taken A pull request linked to this issue is open or already merged. Openaccount-pool bug gui
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
lidge-jun/opencodex#8286 · 2 comments · 1 reaction ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 72/100
Maintainers usually reply within 1 day