feat(composition): add Feeds widget to support multifeed#7004
Merged
feat(composition): add Feeds widget to support multifeed#7004
Conversation
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 455 |
| Duplication | 62 |
TIP This summary will be updated as you push new changes.
* feat(helper): support multifeed composition responses Compositions can return multiple result sets (feeds), each identified by a `feedID`. Previously, `_runComposition` spliced only 1 result per derived helper, discarding additional feeds. This changes `queriesCount` to `Infinity` so all feeds are captured, and builds a `_feedResults` map and `_feedOrder` array on the primary `SearchResults` for downstream consumption by `connectFeeds` (Layer 2). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: bump bundlesize limits for multifeed helper changes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(helper): omit queriesCount for composition instead of using Infinity Use undefined queriesCount to signal "take all results" in _dispatchAlgoliaResponse, avoiding the Infinity hack. When queriesCount is undefined (composition path), use results directly; when defined (regular search path), splice as before. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(helper): remove composition type definitions, defer to layer 2 The feedID, _feedResults, and _feedOrder type definitions have no consumers yet. Defer the typing decision (interface vs class, naming) to Layer 2 when connectFeeds introduces the first consumer. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(helper): use lastResults.feeds array instead of _feedResults map Replace _feedResults (Record<string, SearchResults>) and _feedOrder (string[]) with a single lastResults.feeds (CompositionSearchResults[]) ordered array. This simplifies the API and ensures feeds survive the SSR getInitialResults → JSON → hydration round-trip naturally. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(helper): avoid circular ref in multifeed lastResults Create a separate SearchResults instance for lastResults instead of reusing feeds[0], which caused circular references during JSON.stringify (lastResults.feeds[0] === lastResults). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * apply suggestions from code review Co-authored-by: Haroen Viaene <hello@haroen.me> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Haroen Viaene <hello@haroen.me>
…tions (#6964) * feat(helper): support multifeed composition responses Compositions can return multiple result sets (feeds), each identified by a `feedID`. Previously, `_runComposition` spliced only 1 result per derived helper, discarding additional feeds. This changes `queriesCount` to `Infinity` so all feeds are captured, and builds a `_feedResults` map and `_feedOrder` array on the primary `SearchResults` for downstream consumption by `connectFeeds` (Layer 2). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * chore: bump bundlesize limits for multifeed helper changes Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(helper): omit queriesCount for composition instead of using Infinity Use undefined queriesCount to signal "take all results" in _dispatchAlgoliaResponse, avoiding the Infinity hack. When queriesCount is undefined (composition path), use results directly; when defined (regular search path), splice as before. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(helper): remove composition type definitions, defer to layer 2 The feedID, _feedResults, and _feedOrder type definitions have no consumers yet. Defer the typing decision (interface vs class, naming) to Layer 2 when connectFeeds introduces the first consumer. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(helper): use lastResults.feeds array instead of _feedResults map Replace _feedResults (Record<string, SearchResults>) and _feedOrder (string[]) with a single lastResults.feeds (CompositionSearchResults[]) ordered array. This simplifies the API and ensures feeds survive the SSR getInitialResults → JSON → hydration round-trip naturally. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(helper): avoid circular ref in multifeed lastResults Create a separate SearchResults instance for lastResults instead of reusing feeds[0], which caused circular references during JSON.stringify (lastResults.feeds[0] === lastResults). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * apply suggestions from code review Co-authored-by: Haroen Viaene <hello@haroen.me> * refactor(instantsearch): extract storeRenderState to shared util Move storeRenderState from module-private in index.ts to render-args.ts so FeedContainer can reuse it in the feeds connector (Layer 2). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(instantsearch): centralize index widget types, widen for feedContainer Introduce indexWidgetTypes const and IndexWidgetType as single source of truth for index-like widget types. Update isIndexWidget, IndexWidgetDescription, BuiltinTypes, and BuiltinWidgetTypes to use them. Add 'ais.feeds' and 'ais.feedContainer' to builtin type unions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(metadata): use isIndexWidget to recurse into feed containers The metadata middleware hardcoded 'ais.index' instead of using isIndexWidget, so widgets inside feed containers were invisible to the Algolia Crawler metadata. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * feat(instantsearch): add FeedContainer for per-feed widget subtrees Made-with: Cursor * feat(instantsearch): add connectFeeds connector for multifeed compositions Made-with: Cursor * test(instantsearch): add unit tests for FeedContainer and connectFeeds Made-with: Cursor * chore: bump bundlesize limits for multifeed connector Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(instantsearch): prevent double-init in FeedContainer with initialized flag Replace `instantSearchInstance.started` check with a local `initialized` flag so that child widgets added before `container.init()` are not eagerly initialized. This prevents double-init when the parent index tree calls `init()` on the container after widgets have already been added. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * refactor(instantsearch): simplify connectFeeds to stateless feedIDs computation Remove FeedContainer lifecycle management from the connector — widget layers now own container creation, registration, and cleanup. The connector only computes feedIDs from results.feeds and exposes them as render state. - Remove `widgets` param and internal `feedContainers` map - Change render state from `feeds: [{ feedID, container }]` to `feedIDs: string[]` - Make `getWidgetRenderState` stateless (derives feedIDs from results) - Simplify `dispose()` to only call `unmountFn()` - Extract shared test helpers to `test/createFeedsTestHelpers.ts` Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(instantsearch): fix lint errors in feeds test helpers - Use `import type` for type-only imports - Fix import ordering in test files Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * review: use setState * feedback: chain state when removing widgets (same as in dispose below) * feedback: register FeedsWidgetDescription in IndexRenderState Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Haroen Viaene <hello@haroen.me> Co-authored-by: Dan Rodriguez <drodriguln@icloud.com>
* feat(helper): support multifeed composition responses
Compositions can return multiple result sets (feeds), each identified by
a `feedID`. Previously, `_runComposition` spliced only 1 result per
derived helper, discarding additional feeds. This changes `queriesCount`
to `Infinity` so all feeds are captured, and builds a `_feedResults` map
and `_feedOrder` array on the primary `SearchResults` for downstream
consumption by `connectFeeds` (Layer 2).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: bump bundlesize limits for multifeed helper changes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(helper): omit queriesCount for composition instead of using Infinity
Use undefined queriesCount to signal "take all results" in _dispatchAlgoliaResponse,
avoiding the Infinity hack. When queriesCount is undefined (composition path), use
results directly; when defined (regular search path), splice as before.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(helper): remove composition type definitions, defer to layer 2
The feedID, _feedResults, and _feedOrder type definitions have no consumers
yet. Defer the typing decision (interface vs class, naming) to Layer 2 when
connectFeeds introduces the first consumer.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(helper): use lastResults.feeds array instead of _feedResults map
Replace _feedResults (Record<string, SearchResults>) and _feedOrder
(string[]) with a single lastResults.feeds (CompositionSearchResults[])
ordered array. This simplifies the API and ensures feeds survive the
SSR getInitialResults → JSON → hydration round-trip naturally.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(helper): avoid circular ref in multifeed lastResults
Create a separate SearchResults instance for lastResults instead of
reusing feeds[0], which caused circular references during
JSON.stringify (lastResults.feeds[0] === lastResults).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* apply suggestions from code review
Co-authored-by: Haroen Viaene <hello@haroen.me>
* refactor(instantsearch): extract storeRenderState to shared util
Move storeRenderState from module-private in index.ts to render-args.ts
so FeedContainer can reuse it in the feeds connector (Layer 2).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(instantsearch): centralize index widget types, widen for feedContainer
Introduce indexWidgetTypes const and IndexWidgetType as single source of
truth for index-like widget types. Update isIndexWidget, IndexWidgetDescription,
BuiltinTypes, and BuiltinWidgetTypes to use them. Add 'ais.feeds' and
'ais.feedContainer' to builtin type unions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(metadata): use isIndexWidget to recurse into feed containers
The metadata middleware hardcoded 'ais.index' instead of using
isIndexWidget, so widgets inside feed containers were invisible
to the Algolia Crawler metadata.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(instantsearch): add FeedContainer for per-feed widget subtrees
Made-with: Cursor
* feat(instantsearch): add connectFeeds connector for multifeed compositions
Made-with: Cursor
* test(instantsearch): add unit tests for FeedContainer and connectFeeds
Made-with: Cursor
* chore: bump bundlesize limits for multifeed connector
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(instantsearch): prevent double-init in FeedContainer with initialized flag
Replace `instantSearchInstance.started` check with a local `initialized` flag
so that child widgets added before `container.init()` are not eagerly
initialized. This prevents double-init when the parent index tree calls
`init()` on the container after widgets have already been added.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(instantsearch): simplify connectFeeds to stateless feedIDs computation
Remove FeedContainer lifecycle management from the connector — widget layers
now own container creation, registration, and cleanup. The connector only
computes feedIDs from results.feeds and exposes them as render state.
- Remove `widgets` param and internal `feedContainers` map
- Change render state from `feeds: [{ feedID, container }]` to `feedIDs: string[]`
- Make `getWidgetRenderState` stateless (derives feedIDs from results)
- Simplify `dispose()` to only call `unmountFn()`
- Extract shared test helpers to `test/createFeedsTestHelpers.ts`
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(instantsearch): fix lint errors in feeds test helpers
- Use `import type` for type-only imports
- Fix import ordering in test files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* review: use setState
* feedback: chain state when removing widgets (same as in dispose below)
* feedback: register FeedsWidgetDescription in IndexRenderState
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(instantsearch): add feeds widget for vanilla JS
Add `feeds()` widget that creates per-feed DOM containers and manages
FeedContainer lifecycle. Each feed gets a scoped `<div class="ais-Feeds-feed">`
and its own FeedContainer registered with the parent index.
- DOM creation, reuse, reorder, and cleanup per feed
- Deferred removal with coalesced timer (matches connectDynamicWidgets pattern)
- Dispose merges active + pending containers for clean teardown
- Exported from both main and UMD entrypoints
- 11 unit tests + 1 integration test with real search lifecycle
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(react-instantsearch): add Feeds component and useFeeds hook
Add `<Feeds>` component that creates per-feed `IndexContext.Provider` scopes,
and `useFeeds` hook wrapping `connectFeeds` via `useConnector`.
- FeedContainer creation + registration fused in render body for SSR compat
- Deferred removal with coalesced timer (single ref pattern from useWidget)
- Unmount cleanup merges active + pending containers
- 5 unit tests + 2 integration tests (real InstantSearch + SSR)
- Exported from react-instantsearch-core
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(vue-instantsearch): add AisFeeds component
Add `<ais-feeds>` component with per-feed `AisFeedProvider` scoping via
Vue's provide/inject for `$_ais_getParentIndex`.
- Container reconciliation in watcher (side-effect-free render)
- Deferred removal with coalesced timer (single timer pattern)
- Unmount cleanup merges active + pending containers
- 5 unit tests + 1 integration test with real InstantSearch
- Exported as AisFeeds from vue-instantsearch
- Updated mock and index tests for compositionID support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: fix import ordering lint errors across Layer 3 files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(instantsearch): pass renderOptions to feedContainer.render() for type safety
FeedContainer.render() ignores the argument internally but IndexWidget type
requires it. Pass renderOptionsRef to satisfy TypeScript.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* temp: add examples
* fix: exclude composition examples from v4 type-check
@algolia/composition doesn't exist under algoliasearch v4.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(react-instantsearch-core): rename Feeds children render-prop to renderFeed
Switch from a children render function to a dedicated `renderFeed` prop with
an object argument `({ feedID })`. Keeps the API consistent with other React
InstantSearch components (`hitComponent`, `itemComponent`, `bannerComponent`),
which all use explicit, non-children customization props.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(vue-instantsearch): use indexOf instead of findIndex in mock removeWidgets
findIndex expects a predicate function, not the widget object — the call
threw at runtime the first time removeWidgets was invoked.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix: copilot feedback
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Haroen Viaene <hello@haroen.me>
Co-authored-by: Dan Rodriguez <daniel.rodriguez@algolia.com>
Co-authored-by: Dan Rodriguez <drodriguln@icloud.com>
* feat(helper): support multifeed composition responses
Compositions can return multiple result sets (feeds), each identified by
a `feedID`. Previously, `_runComposition` spliced only 1 result per
derived helper, discarding additional feeds. This changes `queriesCount`
to `Infinity` so all feeds are captured, and builds a `_feedResults` map
and `_feedOrder` array on the primary `SearchResults` for downstream
consumption by `connectFeeds` (Layer 2).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: bump bundlesize limits for multifeed helper changes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(helper): omit queriesCount for composition instead of using Infinity
Use undefined queriesCount to signal "take all results" in _dispatchAlgoliaResponse,
avoiding the Infinity hack. When queriesCount is undefined (composition path), use
results directly; when defined (regular search path), splice as before.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(helper): remove composition type definitions, defer to layer 2
The feedID, _feedResults, and _feedOrder type definitions have no consumers
yet. Defer the typing decision (interface vs class, naming) to Layer 2 when
connectFeeds introduces the first consumer.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(helper): use lastResults.feeds array instead of _feedResults map
Replace _feedResults (Record<string, SearchResults>) and _feedOrder
(string[]) with a single lastResults.feeds (CompositionSearchResults[])
ordered array. This simplifies the API and ensures feeds survive the
SSR getInitialResults → JSON → hydration round-trip naturally.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(helper): avoid circular ref in multifeed lastResults
Create a separate SearchResults instance for lastResults instead of
reusing feeds[0], which caused circular references during
JSON.stringify (lastResults.feeds[0] === lastResults).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* apply suggestions from code review
Co-authored-by: Haroen Viaene <hello@haroen.me>
* refactor(instantsearch): extract storeRenderState to shared util
Move storeRenderState from module-private in index.ts to render-args.ts
so FeedContainer can reuse it in the feeds connector (Layer 2).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(instantsearch): centralize index widget types, widen for feedContainer
Introduce indexWidgetTypes const and IndexWidgetType as single source of
truth for index-like widget types. Update isIndexWidget, IndexWidgetDescription,
BuiltinTypes, and BuiltinWidgetTypes to use them. Add 'ais.feeds' and
'ais.feedContainer' to builtin type unions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(metadata): use isIndexWidget to recurse into feed containers
The metadata middleware hardcoded 'ais.index' instead of using
isIndexWidget, so widgets inside feed containers were invisible
to the Algolia Crawler metadata.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(instantsearch): add FeedContainer for per-feed widget subtrees
Made-with: Cursor
* feat(instantsearch): add connectFeeds connector for multifeed compositions
Made-with: Cursor
* test(instantsearch): add unit tests for FeedContainer and connectFeeds
Made-with: Cursor
* chore: bump bundlesize limits for multifeed connector
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(instantsearch): prevent double-init in FeedContainer with initialized flag
Replace `instantSearchInstance.started` check with a local `initialized` flag
so that child widgets added before `container.init()` are not eagerly
initialized. This prevents double-init when the parent index tree calls
`init()` on the container after widgets have already been added.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(instantsearch): simplify connectFeeds to stateless feedIDs computation
Remove FeedContainer lifecycle management from the connector — widget layers
now own container creation, registration, and cleanup. The connector only
computes feedIDs from results.feeds and exposes them as render state.
- Remove `widgets` param and internal `feedContainers` map
- Change render state from `feeds: [{ feedID, container }]` to `feedIDs: string[]`
- Make `getWidgetRenderState` stateless (derives feedIDs from results)
- Simplify `dispose()` to only call `unmountFn()`
- Extract shared test helpers to `test/createFeedsTestHelpers.ts`
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(instantsearch): fix lint errors in feeds test helpers
- Use `import type` for type-only imports
- Fix import ordering in test files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* review: use setState
* feedback: chain state when removing widgets (same as in dispose below)
* feedback: register FeedsWidgetDescription in IndexRenderState
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(instantsearch): add feeds widget for vanilla JS
Add `feeds()` widget that creates per-feed DOM containers and manages
FeedContainer lifecycle. Each feed gets a scoped `<div class="ais-Feeds-feed">`
and its own FeedContainer registered with the parent index.
- DOM creation, reuse, reorder, and cleanup per feed
- Deferred removal with coalesced timer (matches connectDynamicWidgets pattern)
- Dispose merges active + pending containers for clean teardown
- Exported from both main and UMD entrypoints
- 11 unit tests + 1 integration test with real search lifecycle
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(react-instantsearch): add Feeds component and useFeeds hook
Add `<Feeds>` component that creates per-feed `IndexContext.Provider` scopes,
and `useFeeds` hook wrapping `connectFeeds` via `useConnector`.
- FeedContainer creation + registration fused in render body for SSR compat
- Deferred removal with coalesced timer (single ref pattern from useWidget)
- Unmount cleanup merges active + pending containers
- 5 unit tests + 2 integration tests (real InstantSearch + SSR)
- Exported from react-instantsearch-core
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(vue-instantsearch): add AisFeeds component
Add `<ais-feeds>` component with per-feed `AisFeedProvider` scoping via
Vue's provide/inject for `$_ais_getParentIndex`.
- Container reconciliation in watcher (side-effect-free render)
- Deferred removal with coalesced timer (single timer pattern)
- Unmount cleanup merges active + pending containers
- 5 unit tests + 1 integration test with real InstantSearch
- Exported as AisFeeds from vue-instantsearch
- Updated mock and index tests for compositionID support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: fix import ordering lint errors across Layer 3 files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(instantsearch): pass renderOptions to feedContainer.render() for type safety
FeedContainer.render() ignores the argument internally but IndexWidget type
requires it. Pass renderOptionsRef to satisfy TypeScript.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* temp: add examples
* fix: exclude composition examples from v4 type-check
@algolia/composition doesn't exist under algoliasearch v4.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(react-instantsearch-core): rename Feeds children render-prop to renderFeed
Switch from a children render function to a dedicated `renderFeed` prop with
an object argument `({ feedID })`. Keeps the API consistent with other React
InstantSearch components (`hitComponent`, `itemComponent`, `bannerComponent`),
which all use explicit, non-children customization props.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(vue-instantsearch): use indexOf instead of findIndex in mock removeWidgets
findIndex expects a predicate function, not the widget object — the call
threw at runtime the first time removeWidgets was invoked.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(instantsearch): extract isTwoPassWidget utility
Replace inline `$$type === 'ais.dynamicWidgets'` checks with a shared
`isTwoPassWidget` predicate. This prepares for the feeds widget which
also requires a two-pass SSR cycle, and fixes a missing `shouldRefetch ||`
in InitializePromise.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(instantsearch): add SSR support for composition multifeed
Serialize per-feed results as `compositionFeedsResults` in
`getInitialResults`, hydrate them in `hydrateSearchClient`, and
reconstruct `lastResults.feeds` in the feeds connector so that
composition multifeed works with server-side rendering.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(instantsearch): add composition multifeed SSR integration test
End-to-end integration test covering the full SSR cycle: server-side
rendering with getInitialResults, hydration via hydrateSearchClient,
and client-side rehydration of per-feed results.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: add Next.js App Router composition example
Demo app showing composition multifeed with SSR using
react-instantsearch-nextjs. Excluded from v4 type-check
since it uses composition-only APIs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(react-instantsearch-core): adapt Feeds usages to renderFeed prop
Update the SSR test and the Next.js App Router composition example to the
new `renderFeed` prop introduced on Layer 3.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: bump bundlesize limit for SSR multifeed
The composition multifeed SSR additions push the development bundle just
over the 256.5 kB ceiling.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Haroen Viaene <hello@haroen.me>
Co-authored-by: Dan Rodriguez <drodriguln@icloud.com>
61c8811 to
cab5256
Compare
More templates
algoliasearch-helper
instantsearch-ui-components
instantsearch.css
instantsearch.js
react-instantsearch
react-instantsearch-core
react-instantsearch-nextjs
react-instantsearch-router-nextjs
vue-instantsearch
commit: |
* feat(helper): support multifeed composition responses
Compositions can return multiple result sets (feeds), each identified by
a `feedID`. Previously, `_runComposition` spliced only 1 result per
derived helper, discarding additional feeds. This changes `queriesCount`
to `Infinity` so all feeds are captured, and builds a `_feedResults` map
and `_feedOrder` array on the primary `SearchResults` for downstream
consumption by `connectFeeds` (Layer 2).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: bump bundlesize limits for multifeed helper changes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(helper): omit queriesCount for composition instead of using Infinity
Use undefined queriesCount to signal "take all results" in _dispatchAlgoliaResponse,
avoiding the Infinity hack. When queriesCount is undefined (composition path), use
results directly; when defined (regular search path), splice as before.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(helper): remove composition type definitions, defer to layer 2
The feedID, _feedResults, and _feedOrder type definitions have no consumers
yet. Defer the typing decision (interface vs class, naming) to Layer 2 when
connectFeeds introduces the first consumer.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(helper): use lastResults.feeds array instead of _feedResults map
Replace _feedResults (Record<string, SearchResults>) and _feedOrder
(string[]) with a single lastResults.feeds (CompositionSearchResults[])
ordered array. This simplifies the API and ensures feeds survive the
SSR getInitialResults → JSON → hydration round-trip naturally.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(helper): avoid circular ref in multifeed lastResults
Create a separate SearchResults instance for lastResults instead of
reusing feeds[0], which caused circular references during
JSON.stringify (lastResults.feeds[0] === lastResults).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* apply suggestions from code review
Co-authored-by: Haroen Viaene <hello@haroen.me>
* refactor(instantsearch): extract storeRenderState to shared util
Move storeRenderState from module-private in index.ts to render-args.ts
so FeedContainer can reuse it in the feeds connector (Layer 2).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(instantsearch): centralize index widget types, widen for feedContainer
Introduce indexWidgetTypes const and IndexWidgetType as single source of
truth for index-like widget types. Update isIndexWidget, IndexWidgetDescription,
BuiltinTypes, and BuiltinWidgetTypes to use them. Add 'ais.feeds' and
'ais.feedContainer' to builtin type unions.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(metadata): use isIndexWidget to recurse into feed containers
The metadata middleware hardcoded 'ais.index' instead of using
isIndexWidget, so widgets inside feed containers were invisible
to the Algolia Crawler metadata.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(instantsearch): add FeedContainer for per-feed widget subtrees
Made-with: Cursor
* feat(instantsearch): add connectFeeds connector for multifeed compositions
Made-with: Cursor
* test(instantsearch): add unit tests for FeedContainer and connectFeeds
Made-with: Cursor
* chore: bump bundlesize limits for multifeed connector
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(instantsearch): prevent double-init in FeedContainer with initialized flag
Replace `instantSearchInstance.started` check with a local `initialized` flag
so that child widgets added before `container.init()` are not eagerly
initialized. This prevents double-init when the parent index tree calls
`init()` on the container after widgets have already been added.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(instantsearch): simplify connectFeeds to stateless feedIDs computation
Remove FeedContainer lifecycle management from the connector — widget layers
now own container creation, registration, and cleanup. The connector only
computes feedIDs from results.feeds and exposes them as render state.
- Remove `widgets` param and internal `feedContainers` map
- Change render state from `feeds: [{ feedID, container }]` to `feedIDs: string[]`
- Make `getWidgetRenderState` stateless (derives feedIDs from results)
- Simplify `dispose()` to only call `unmountFn()`
- Extract shared test helpers to `test/createFeedsTestHelpers.ts`
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(instantsearch): fix lint errors in feeds test helpers
- Use `import type` for type-only imports
- Fix import ordering in test files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* review: use setState
* feedback: chain state when removing widgets (same as in dispose below)
* feedback: register FeedsWidgetDescription in IndexRenderState
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(instantsearch): add feeds widget for vanilla JS
Add `feeds()` widget that creates per-feed DOM containers and manages
FeedContainer lifecycle. Each feed gets a scoped `<div class="ais-Feeds-feed">`
and its own FeedContainer registered with the parent index.
- DOM creation, reuse, reorder, and cleanup per feed
- Deferred removal with coalesced timer (matches connectDynamicWidgets pattern)
- Dispose merges active + pending containers for clean teardown
- Exported from both main and UMD entrypoints
- 11 unit tests + 1 integration test with real search lifecycle
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(react-instantsearch): add Feeds component and useFeeds hook
Add `<Feeds>` component that creates per-feed `IndexContext.Provider` scopes,
and `useFeeds` hook wrapping `connectFeeds` via `useConnector`.
- FeedContainer creation + registration fused in render body for SSR compat
- Deferred removal with coalesced timer (single ref pattern from useWidget)
- Unmount cleanup merges active + pending containers
- 5 unit tests + 2 integration tests (real InstantSearch + SSR)
- Exported from react-instantsearch-core
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(vue-instantsearch): add AisFeeds component
Add `<ais-feeds>` component with per-feed `AisFeedProvider` scoping via
Vue's provide/inject for `$_ais_getParentIndex`.
- Container reconciliation in watcher (side-effect-free render)
- Deferred removal with coalesced timer (single timer pattern)
- Unmount cleanup merges active + pending containers
- 5 unit tests + 1 integration test with real InstantSearch
- Exported as AisFeeds from vue-instantsearch
- Updated mock and index tests for compositionID support
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: fix import ordering lint errors across Layer 3 files
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(instantsearch): pass renderOptions to feedContainer.render() for type safety
FeedContainer.render() ignores the argument internally but IndexWidget type
requires it. Pass renderOptionsRef to satisfy TypeScript.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* temp: add examples
* fix: exclude composition examples from v4 type-check
@algolia/composition doesn't exist under algoliasearch v4.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(react-instantsearch-core): rename Feeds children render-prop to renderFeed
Switch from a children render function to a dedicated `renderFeed` prop with
an object argument `({ feedID })`. Keeps the API consistent with other React
InstantSearch components (`hitComponent`, `itemComponent`, `bannerComponent`),
which all use explicit, non-children customization props.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix(vue-instantsearch): use indexOf instead of findIndex in mock removeWidgets
findIndex expects a predicate function, not the widget object — the call
threw at runtime the first time removeWidgets was invoked.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(instantsearch): extract isTwoPassWidget utility
Replace inline `$$type === 'ais.dynamicWidgets'` checks with a shared
`isTwoPassWidget` predicate. This prepares for the feeds widget which
also requires a two-pass SSR cycle, and fixes a missing `shouldRefetch ||`
in InitializePromise.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat(instantsearch): add SSR support for composition multifeed
Serialize per-feed results as `compositionFeedsResults` in
`getInitialResults`, hydrate them in `hydrateSearchClient`, and
reconstruct `lastResults.feeds` in the feeds connector so that
composition multifeed works with server-side rendering.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test(instantsearch): add composition multifeed SSR integration test
End-to-end integration test covering the full SSR cycle: server-side
rendering with getInitialResults, hydration via hydrateSearchClient,
and client-side rehydration of per-feed results.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: add Next.js App Router composition example
Demo app showing composition multifeed with SSR using
react-instantsearch-nextjs. Excluded from v4 type-check
since it uses composition-only APIs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* refactor(react-instantsearch-core): adapt Feeds usages to renderFeed prop
Update the SSR test and the Next.js App Router composition example to the
new `renderFeed` prop introduced on Layer 3.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* chore: bump bundlesize limit for SSR multifeed
The composition multifeed SSR additions push the development bundle just
over the 256.5 kB ceiling.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore: remove internal composition examples
These examples reference an internal Algolia application and should not
be published.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: reapply removal commits after merge commit
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Haroen Viaene <hello@haroen.me>
Co-authored-by: Dan Rodriguez <drodriguln@icloud.com>
Co-authored-by: Dan Rodriguez <daniel.rodriguez@algolia.com>
Haroenv
approved these changes
May 4, 2026
FabienMotte
approved these changes
May 4, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the Feeds widget for JS, React, and Vue, which provides support for multifeed compositions. Implements the changes from the following PRs which have been merged into this one. This does not include changes for Autocomplete--that will come later.
#6954
#6964
#6969
#6975
#6977
Result
Reference the linked PRs for details, but otherwise the CI and preview should be ✅ .