Skip to content

feat(vue): add useReactiveSlots — Vue-reactive slot evaluation - #88

Merged
kibertoad merged 3 commits into
mainfrom
feat/vue-reactive-slots
Jul 18, 2026
Merged

feat(vue): add useReactiveSlots — Vue-reactive slot evaluation#88
kibertoad merged 3 commits into
mainfrom
feat/vue-reactive-slots

Conversation

@kibertoad

@kibertoad kibertoad commented Jul 18, 2026

Copy link
Copy Markdown
Owner

What

Adds useReactiveSlots() to @modular-vue/vue: the resolved slot manifest as a Vue computed, re-evaluated automatically whenever the reactive state its dynamicSlots factories / slotFilter read changes — no recalculateSlots() call. It rebuilds the deps snapshot inside a tracked computed, so a factory/filter that reads a reactive source live (a service object with getters over refs, a reactive service, a reactive store proxy) becomes a tracked dependency of the computed. Lazy, cached, fine-grained.

This is the Vue-idiomatic counterpart to the existing framework-neutral signal path (useSlots() + useRecalculateSlots()), which is left untouched. Additive: the two coexist and are chosen per source.

Why

The React binding has only the signal path (React has no ambient reactivity to track). In Vue, gating logic whose inputs are reactive state (RBAC permissions, connection-availability flags, feature toggles) would otherwise need a recalculateSlots() at every mutation site — easy to forget, and coarse (rebuilds the whole manifest). A computed gives the same producer-driven invalidation automatically, tracking only the state that changed.

Driven by the cat-factory nav/command-manifest adoption — the production consumer exercising the layer-extends story (docs/consumer-feedback-production-app.md role).

When to use which (documented in full)

Inputs Path
Vue-reactive state the host owns (RBAC, availability) useReactiveSlots
Non-reactive Store/zustand snapshot or external subscribe/getSnapshot signal
Transactional (recompute once after async multi-step) signal
Imperative event, not persisted reactive state signal

Tradeoffs, the "is it over-eager? no" analysis, and the host-owned RBAC-gating shape live in docs/reactive-slots-vue.md (linked from framework-mode-nuxt.md, the @modular-vue/vue README, and the vue-support-tracker).

Changes

  • @modular-vue/vue: useReactiveSlots, reactiveSlotsConfigKey, ReactiveSlotsConfig (in slots-context.ts), exported.
  • @modular-vue/runtime: providers install reactiveSlotsConfigKey (base slots + factories + filter) in both plugin and framework-mode component forms; re-exported.
  • Tests: unit coverage in slots-context.test.ts (reactive recompute with no signal; reactive slotFilter) + an end-to-end reactive-slots.test.ts (resolve → install → reactive filter recomputes).
  • Docs: new docs/reactive-slots-vue.md; links added; tracker updated (partially advances D3 Pinia interop).

Vue-impact note

Vue-only addition on top of the shared engine; no @modular-frontend/* change. React parity unaffected (React keeps signal-only by design).

React source for intent: packages/react/src/slots-context.tsx.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added useReactiveSlots() for automatic Vue-reactive slot updates when permissions, availability, or other reactive inputs change.
    • Added public exports and configuration support for reactive slot evaluation.
    • Reactive slot filtering now supports RBAC-style navigation and command-shell scenarios without manual refreshes.
  • Documentation

    • Added comprehensive guidance on reactive versus signal-based slot evaluation.
    • Updated Vue support tracking, README content, and related framework documentation.
  • Tests

    • Added coverage for automatic updates, reactive filtering, dependency tracking, and usage validation.

Adds `useReactiveSlots()` to `@modular-vue/vue`: the resolved slot manifest as a
Vue `computed`, re-evaluated automatically when the reactive state its
`dynamicSlots` factories / `slotFilter` read changes — no `recalculateSlots()`
call required. It rebuilds the deps snapshot inside a tracked `computed`, so a
factory/filter that reads a reactive source live (a service object with getters
over refs, a reactive service, a reactive store proxy) makes it a tracked
dependency.

This is the Vue-idiomatic counterpart to the existing framework-neutral signal
path (`useSlots()` + `useRecalculateSlots()`), which stays unchanged. The two
coexist and are chosen per source: reactive when the gating inputs are reactive
Vue state the host owns (RBAC permissions, availability flags); signal for
non-reactive/external sources, transactional recompute, or event-driven
invalidation.

The runtime provides a new `reactiveSlotsConfigKey` (base slots + factories +
filter) alongside the existing slots context, in both the plugin and
framework-mode component install forms.

Driven by the cat-factory nav/command-manifest adoption (the production consumer
exercising the layer-extends consumer story). Full tradeoffs +
the host-owned RBAC-gating shape: docs/reactive-slots-vue.md.

React source for intent: packages/react/src/slots-context.tsx (signal-only; Vue
adds the reactive path on top).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@kibertoad, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 43 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 782d8a37-dbd6-46bd-b4e4-971efe0fbbec

📥 Commits

Reviewing files that changed from the base of the PR and between 6bf7d01 and 8a60105.

📒 Files selected for processing (7)
  • docs/reactive-slots-vue.md
  • docs/vue-support-tracker.md
  • packages/vue-runtime/src/index.ts
  • packages/vue-runtime/src/providers.ts
  • packages/vue/src/index.ts
  • packages/vue/src/slots-context.test.ts
  • packages/vue/src/slots-context.ts
📝 Walkthrough

Walkthrough

Adds useReactiveSlots for Vue computed slot evaluation, provides its configuration through the runtime, exports the new API and type, adds unit and end-to-end coverage, and documents reactive versus signal-based slot resolution with RBAC examples.

Changes

Reactive Vue slots

Layer / File(s) Summary
Composable contract and evaluation
packages/vue/src/slots-context.ts, packages/vue/src/index.ts, packages/vue/src/slots-context.test.ts
Defines ReactiveSlotsConfig and reactiveSlotsConfigKey, implements useReactiveSlots with dependency-tracked computed evaluation, exports the API, and tests missing-context, reactive source, filter, and service snapshot behavior.
Runtime provisioning and integration tests
packages/vue-runtime/src/providers.ts, packages/vue-runtime/src/index.ts, packages/vue-runtime/src/reactive-slots.test.ts
Provides slot configuration through modular contexts, re-exports the composable and type, and verifies reactive RBAC filtering in a mounted Vue Router view.
Reactive slots documentation
docs/reactive-slots-vue.md, packages/vue/README.md, docs/vue-support-tracker.md, docs/framework-mode-nuxt.md
Documents computed and signal slot paths, dependency tracking, recomputation, RBAC gating, API behavior, and related framework examples.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant VueComponent
  participant useReactiveSlots
  participant ModularRuntime
  participant evaluateDynamicSlots
  VueComponent->>useReactiveSlots: read computed slot manifest
  useReactiveSlots->>ModularRuntime: inject slot configuration and dependencies
  useReactiveSlots->>evaluateDynamicSlots: evaluate factories and filter
  evaluateDynamicSlots-->>useReactiveSlots: filtered slots
  useReactiveSlots-->>VueComponent: computed slot value
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: adding useReactiveSlots for Vue-reactive slot evaluation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/vue-reactive-slots

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.

- Fix oxfmt formatting that failed the Lint CI job (reactive-slots-vue.md,
  framework-mode-nuxt.md, reactive-slots.test.ts).
- Docs: clarify the "value-gated downstream" note (useReactiveSlots returns a
  plain computed producing a fresh manifest per recompute; value-gating pays off
  one level down on stable derived values), and add a "cost scales per consumer"
  caveat with the share-once-high-in-the-tree remedy.
- Tests: cover the reactiveService getSnapshot() reactive-tracking path so the
  buildDepsSnapshot-through-getSnapshot boundary is locked in alongside the
  existing plain-service and slotFilter cases.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Py3ZFw7u2Qnw9N4X9MRsBi

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

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 `@docs/reactive-slots-vue.md`:
- Around line 130-133: Rewrite the reactive-slots explanation around the gates
getter, slotFilter, and useReactiveSlots computed to state that gates is a plain
service passed by reference and dependency tracking occurs when its getter reads
reactive state during computed evaluation. Remove the suggestion that an
attached dependency snapshot changes or must be refreshed or replaced, while
preserving the point that consumers of slots.value.nav recompute without
recalculateSlots().
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 32557f56-2ad5-4395-b5c4-3e0219b8b614

📥 Commits

Reviewing files that changed from the base of the PR and between 9078c0b and 6bf7d01.

📒 Files selected for processing (10)
  • docs/framework-mode-nuxt.md
  • docs/reactive-slots-vue.md
  • docs/vue-support-tracker.md
  • packages/vue-runtime/src/index.ts
  • packages/vue-runtime/src/providers.ts
  • packages/vue-runtime/src/reactive-slots.test.ts
  • packages/vue/README.md
  • packages/vue/src/index.ts
  • packages/vue/src/slots-context.test.ts
  • packages/vue/src/slots-context.ts

Comment thread docs/reactive-slots-vue.md Outdated
Make the reactive path mirror the signal path's architecture: the runtime
resolves the manifest once at install into a single shared `computed` and
provides it via `reactiveSlotsKey`; `useReactiveSlots()` becomes a thin reader
over that one source. Previously each `useReactiveSlots()` call built its own
`computed`, so N consumers re-evaluated the factories/filter N times per change;
now evaluation happens once per change regardless of consumer count, and every
consumer sees the same manifest object.

- slots-context.ts: replace `reactiveSlotsConfigKey`/`ReactiveSlotsConfig` with
  `reactiveSlotsKey` (holds the resolved `ComputedRef`) and a runtime-facing
  `resolveReactiveSlots(input)` building block; `useReactiveSlots` injects and
  returns the shared computed. Drops the composable's dependency on
  `sharedDependenciesKey`.
- providers.ts: build the source once per install — inside a detached
  `effectScope` stopped on `app.onUnmount` (plugin form) or the ModularProviders
  `setup` scope (framework-mode) — so the computed's effect is disposed with the
  app, matching how the signal subscription is disposed.
- Tests: cover `resolveReactiveSlots` evaluation directly (reactive factory,
  reactive slotFilter, reactiveService getSnapshot tracking) and assert
  `useReactiveSlots` hands every consumer the same shared computed instance.
- Docs: describe the single-shared-source model; address review feedback on the
  RBAC section — `gates` is a plain service passed by reference, tracked because
  its getters read reactive state inside the computed, not via a snapshot swap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Py3ZFw7u2Qnw9N4X9MRsBi
@kibertoad
kibertoad merged commit 9b6219b into main Jul 18, 2026
18 checks passed
@kibertoad
kibertoad deleted the feat/vue-reactive-slots branch July 18, 2026 15:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants