Skip to content

✨ feat(optimization): [NT-3604] Implement empty variant handling in entry resol…#359

Merged
Lotfi Anwar L Arif (Lotfi-Arif) merged 15 commits into
mainfrom
NT-3604-handle-empty-variant-case-so-that-empty-variants-will-be-hidden-not-rendered
Jul 8, 2026
Merged

✨ feat(optimization): [NT-3604] Implement empty variant handling in entry resol…#359
Lotfi Anwar L Arif (Lotfi-Arif) merged 15 commits into
mainfrom
NT-3604-handle-empty-variant-case-so-that-empty-variants-will-be-hidden-not-rendered

Conversation

@Lotfi-Arif

Copy link
Copy Markdown
Contributor
  • 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.

…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.
@Lotfi-Arif

Copy link
Copy Markdown
Contributor Author

I am still testing this locally with my own setup to make sure

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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)]
Felipe Mamud (fmamud) and others added 6 commits July 8, 2026 16:21
* 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)]
… 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.
* in the Contentful CDA `nt_config` payload. Using `id === ''` catches both forms
* and is stable across all data sources.
*/
isEmptyVariant?: true

@fmamud Felipe Mamud (fmamud) Jul 8, 2026

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.

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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The kind discriminated union is appealing but the Experience API gives us no extra information to justify the extra variants

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.

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.

@Lotfi-Arif

Copy link
Copy Markdown
Contributor Author

Charles Hudson (@phobetron) everything is ready and tested, please approve and lets merge it

@Lotfi-Arif Lotfi Anwar L Arif (Lotfi-Arif) merged commit 3b27777 into main Jul 8, 2026
37 checks passed
@Lotfi-Arif Lotfi Anwar L Arif (Lotfi-Arif) deleted the NT-3604-handle-empty-variant-case-so-that-empty-variants-will-be-hidden-not-rendered branch July 8, 2026 11:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants