✨ feat(optimization): [NT-3604] Implement empty variant handling in entry resol…#359
Conversation
…ution - Introduced support for an **empty variant** (id: "") in the entry personalization and variant resolution process. - Updated the resolver to return an `isEmptyVariant` flag in `ResolvedData`, allowing renderers to suppress content while still emitting component view impressions. - Enhanced documentation to clarify the behavior of empty variants and their distinction from baseline selections and resolution errors. - Added tests to ensure correct handling of empty variants, including logging behavior and tracking context preservation. This update improves the personalization experience by allowing content authors to intentionally show no content for specific audiences while maintaining tracking capabilities.
|
I am still testing this locally with my own setup to make sure |
…timization metrics
Charles Hudson (phobetron)
left a comment
There was a problem hiding this comment.
Looks good!
…andling in personalization and variant resolution - Updated documentation to specify the distinction between empty variants and data errors, detailing how empty variants are represented in the Personalization UI and their detection mechanism. - Enhanced the Zod schema to reflect the dual states of the `hidden` flag in Contentful-sourced data and emphasized the use of `id === ""` for detecting empty variants. - Revised the resolver comments to clarify the handling of empty variants and their implications for rendering and tracking. These changes improve the understanding of empty variant behavior, ensuring accurate implementation and measurement in personalization scenarios.
* feat(web-sdk_angular): Align SSR with isomorphic runtime seam * refactor(web-sdk,react-web-sdk): Move WebOptimizationRuntime to optimization-web/runtime * refactor(web-sdk_angular): Resolve merge tags at render time * refactor(web-sdk_angular): Split tracking-log event switch into methods * chore(web-sdk): Raise runtime subpath bundle-size budgets
Add Core `contentful` configuration, cached `fetchContentfulEntry()`, and `fetchOptimizedEntry()` APIs for stateful and request-bound stateless SDK flows. Introduce shared entry-source lifecycle support, optimized-entry metadata, and managed-entry keying so Web Components, React Web, React Native, and Next.js helpers can resolve entries from `entryId`/`entryQuery` while preserving the manual `baselineEntry` path. Add React Web SSR and hybrid entry handoff support with `serverOptimizedEntries`, prefetch helpers, snapshot-runtime fetch guards, and hydration-safe managed entry lookup. Add Next.js App Router and Pages Router managed-entry support, including server-rendered `<OptimizedEntry entryId>`, Pages Router `prefetchOptimizedEntries`, server-fetched entry handoffs, and client-safe provider config. Refresh docs, guides, reference implementations, and tests for managed entry fetching across Core, Web Components, React Web, React Native, and Next.js. Adjust the Web Components UMD gzip budget for the managed-entry surface while keeping event constants readable. [[NT-3558](https://contentful.atlassian.net/browse/NT-3558)]
Next.js 15 rejects wildcard re-exports across client boundaries, which breaks live updates and preview panel usage when SDK client entries are compiled into a client graph. Replace Next.js SDK client-boundary wildcard barrels with explicit named exports and keep router entrypoints scoped to router factories and route trackers. Remove the ambiguous package-root export so consumers must choose the correct server, client, router, schema, ESR, request-handler, or tracking subpath. Add a shared build-tools guard that fails when a `"use client"` file contains `export *`, and run it from the React Web and Next.js SDK builds. Update tests and documentation for the new import boundaries. BREAKING CHANGE: `@contentful/optimization-nextjs` no longer exports from the package root. Use explicit subpaths such as `/app-router`, `/pages-router`, `/pages-router/server`, `/client`, `/server`, `/api-schemas`, `/esr`, `/request-handler`, or `/tracking-attributes`. BREAKING CHANGE: `/app-router` and `/pages-router` no longer re-export generic React Web hooks, providers, or components. Import those from `/client`. [[NT-3614](https://contentful.atlassian.net/browse/NT-3614)]
* feat(web-sdk_angular): Align SSR with isomorphic runtime seam * refactor(web-sdk,react-web-sdk): Move WebOptimizationRuntime to optimization-web/runtime * refactor(web-sdk_angular): Resolve merge tags at render time * refactor(web-sdk_angular): Split tracking-log event switch into methods * chore(web-sdk): Raise runtime subpath bundle-size budgets
Add Core `contentful` configuration, cached `fetchContentfulEntry()`, and `fetchOptimizedEntry()` APIs for stateful and request-bound stateless SDK flows. Introduce shared entry-source lifecycle support, optimized-entry metadata, and managed-entry keying so Web Components, React Web, React Native, and Next.js helpers can resolve entries from `entryId`/`entryQuery` while preserving the manual `baselineEntry` path. Add React Web SSR and hybrid entry handoff support with `serverOptimizedEntries`, prefetch helpers, snapshot-runtime fetch guards, and hydration-safe managed entry lookup. Add Next.js App Router and Pages Router managed-entry support, including server-rendered `<OptimizedEntry entryId>`, Pages Router `prefetchOptimizedEntries`, server-fetched entry handoffs, and client-safe provider config. Refresh docs, guides, reference implementations, and tests for managed entry fetching across Core, Web Components, React Web, React Native, and Next.js. Adjust the Web Components UMD gzip budget for the managed-entry surface while keeping event constants readable. [[NT-3558](https://contentful.atlassian.net/browse/NT-3558)]
Next.js 15 rejects wildcard re-exports across client boundaries, which breaks live updates and preview panel usage when SDK client entries are compiled into a client graph. Replace Next.js SDK client-boundary wildcard barrels with explicit named exports and keep router entrypoints scoped to router factories and route trackers. Remove the ambiguous package-root export so consumers must choose the correct server, client, router, schema, ESR, request-handler, or tracking subpath. Add a shared build-tools guard that fails when a `"use client"` file contains `export *`, and run it from the React Web and Next.js SDK builds. Update tests and documentation for the new import boundaries. BREAKING CHANGE: `@contentful/optimization-nextjs` no longer exports from the package root. Use explicit subpaths such as `/app-router`, `/pages-router`, `/pages-router/server`, `/client`, `/server`, `/api-schemas`, `/esr`, `/request-handler`, or `/tracking-attributes`. BREAKING CHANGE: `/app-router` and `/pages-router` no longer re-export generic React Web hooks, providers, or components. Import those from `/client`. [[NT-3614](https://contentful.atlassian.net/browse/NT-3614)]
…pty-variants-will-be-hidden-not-rendered
…timization-web components
… error handling and loading states - Introduced `OptimizedEntryBodyProps` interface for better type safety. - Created `renderOptimizedEntryBody` function to encapsulate rendering logic for different states (error, loading, resolved). - Updated `OptimizedEntry` component to utilize the new rendering function, enhancing readability and maintainability. - Improved handling of duplicate baseline ancestors and loading fallbacks.
…js to 4800 and 4100 respectively
| * in the Contentful CDA `nt_config` payload. Using `id === ''` catches both forms | ||
| * and is stable across all data sources. | ||
| */ | ||
| isEmptyVariant?: true |
There was a problem hiding this comment.
I'm wondering if we can represent the variant emptiness by using a state/type property (e.g. kind) on resolved data instead of an optional boolean flag. WDYT?
For example:
type ResolvedData<S, M, L> =
| { kind: 'baseline'; entry: Entry<S, M, L>; selectedOptimization?: SelectedOptimization }
| { kind: 'variant'; entry: Entry<S, M, L>; selectedOptimization: SelectedOptimization }
| { kind: 'empty'; entry: Entry<S, M, L>; selectedOptimization: SelectedOptimization }Renderers become if (resolved.kind === 'empty') return <...>, switches could became exhaustive so future outcomes surface as compile errors, and selectedOptimization narrows to non-optional where it's actually guaranteed.
There was a problem hiding this comment.
I feel like that could require unexpected maintenance over time, as well as require extra logic to determine whether each is entry is of a specific type, and that type isn't always straightforward (baseline can be thought of as a variant in many cases). The current solution fix the exact case without affecting any other feature, and it's a bit of a unique case as it is. I could be wrong though.
There was a problem hiding this comment.
The kind discriminated union is appealing but the Experience API gives us no extra information to justify the extra variants
There was a problem hiding this comment.
I see, but I was thinking in a way that customers would need to know if a variant is empty or not by checking the type/kind, otherwise no need to care about it. I feel like having a optional boolean flag a bit dubious as part of a public API.
|
Charles Hudson (@phobetron) everything is ready and tested, please approve and lets merge it |
isEmptyVariantflag inResolvedData, allowing renderers to suppress content while still emitting component view impressions.This update improves the personalization experience by allowing content authors to intentionally show no content for specific audiences while maintaining tracking capabilities.