Skip to content

feat(App): add useId prop to customize Reka UI id generation#6569

Draft
Mat4m0 wants to merge 3 commits into
nuxt:v4from
Mat4m0:fix/reka-stable-ids
Draft

feat(App): add useId prop to customize Reka UI id generation#6569
Mat4m0 wants to merge 3 commits into
nuxt:v4from
Mat4m0:fix/reka-stable-ids

Conversation

@Mat4m0

@Mat4m0 Mat4m0 commented Jun 8, 2026

Copy link
Copy Markdown

🔗 Linked issue

Resolves #5801 #5529 (maybe)

❓ Type of change

  • 📖 Documentation (updates to the documentation or readme)
  • 🐞 Bug fix (a non-breaking change that fixes an issue)
  • 👌 Enhancement (improving an existing functionality)
  • ✨ New feature (a non-breaking change that adds functionality)
  • 🧹 Chore (updates to the build process or auxiliary tools and libraries)
  • ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

📚 Description

UApp currently passes Vue's useId through to Reka's ConfigProvider.

In Nuxt prerendered output, Vue's app ID sequence/prefix can differ between the server-rendered HTML and the client hydration pass. Reka primitives derive DOM IDs from their configured ID source, so components such as Accordion and Tabs can hydrate with different id, aria-controls, and aria-labelledby values than the prerendered HTML.

This PR makes UApp provide a stable, instance-scoped Reka ID generator instead:

let id = 0
const useRekaId = () => `nuxt-ui-${++id}`

The generator is scoped to the UApp render instance, so SSR and client hydration replay the same sequence for Reka IDs instead of depending on Vue's app-level ID source.

This is the Nuxt UI side of the fix: Nuxt UI provides the stable app-level ID source through the existing Reka ConfigProvider.useId API.

The matching Reka UI fix (unovue/reka-ui#2683) is also required so Reka treats ConfigProvider.useId as the canonical source before falling back to Vue's useId. Without that Reka-side precedence change, Nuxt UI can provide a stable generator but current Reka versions may still prefer Vue's useId internally.

This does not add a new public Nuxt UI API.

Tested locally with a Nuxt prerender reproduction using UApp plus Reka Accordion/Tabs primitives:

  • pnpm generate
  • served .output/public
  • checked the hydrated page in Chrome/Playwright

Before the Nuxt UI + Reka UI fixes, hydration reported Reka ID mismatches. With both fixes applied, there were no hydration warnings/errors.

📝 Checklist

  • I have linked an issue or discussion.
  • I have updated the documentation accordingly.

@Mat4m0 Mat4m0 requested a review from benjamincanac as a code owner June 8, 2026 14:36
@github-actions github-actions Bot added the v4 #4488 label Jun 8, 2026
@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

App.vue replaces Vue's useId with a component-local incrementing counter for stable ID generation. The useId import is removed, and a new useRekaId generator function is introduced, backed by an incrementing id counter scoped to the UApp render instance. The ConfigProvider's :use-id prop is updated to use this new generator instead of the Vue function, ensuring consistent ID sequences across SSR and client hydration.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title mentions adding a 'useId prop' but the actual change is implementing a stable local ID generator to fix hydration mismatches—not exposing a customizable prop as the title suggests. Update the title to reflect the actual change, such as 'fix(app): provide stable Reka IDs during hydration' to match the PR's core objective of fixing hydration mismatches.
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description clearly explains the bug being fixed (Vue's useId causing hydration mismatches in prerendered Nuxt output), the solution implemented (stable instance-scoped Reka ID generator), testing performed, and related dependencies.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/runtime/components/App.vue`:
- Line 55: Upgrade the reka-ui dependency to the release that contains the
ConfigProvider.useId priority fix (the commit referenced in reka-ui#2683) so
that ConfigProvider :use-id="useRekaId" in App.vue is honored on Vue 3.5+;
update package.json to the fixed version, regenerate the lockfile (npm/yarn/pnpm
install) and verify that ConfigProvider (used in src/runtime/components/App.vue)
now respects the useRekaId prop rather than preferring Vue's global useId.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 67d9d3e6-bbd0-4770-bc7a-b709dc4b69aa

📥 Commits

Reviewing files that changed from the base of the PR and between 52367b1 and 34aae00.

📒 Files selected for processing (1)
  • src/runtime/components/App.vue

Comment thread src/runtime/components/App.vue Outdated
@pkg-pr-new

pkg-pr-new Bot commented Jun 8, 2026

Copy link
Copy Markdown
npm i https://pkg.pr.new/@nuxt/ui@6569

commit: 13f2767

@Mat4m0

Mat4m0 commented Jun 11, 2026

Copy link
Copy Markdown
Author

The fix for the ConfigProvider is released in: https://github.com/unovue/reka-ui/releases/tag/v2.9.10

@benjamincanac

Copy link
Copy Markdown
Member

@Mat4m0 It's merged 😊

@benjamincanac benjamincanac changed the title fix(app): provide stable Reka IDs during hydration feat(App): add useId prop to customize Reka UI id generation Jun 12, 2026
@benjamincanac benjamincanac changed the title feat(App): add useId prop to customize Reka UI id generation feat(App): add useId prop to customize Reka UI id generation Jun 12, 2026
@benjamincanac

Copy link
Copy Markdown
Member

Thanks for digging into this @Mat4m0!

I tested the counter approach a bit more and I don't think we can make it the default: it only works when Reka primitives mount in the same order on server and client, which breaks as soon as async subtrees resolve out of order (two sibling components awaiting useFetch with different latencies for example). The actual root cause is probably upstream in vuejs/core#12591 anyway.

I've reworked your branch to expose the ConfigProvider useId prop on UApp instead: the default stays Vue's useId so nothing changes for existing apps, but you can now opt into a stable generator.

Let me know what you think of this direction!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v4 #4488

Projects

None yet

Development

Successfully merging this pull request may close these issues.

<UHeader> causing Hydration error when using with e.g. <LazyAccordion>

2 participants