Optimizely Content JS SDK v2.1.0 — What's New and Why It Matters
v2.1.0 of the Optimizely Content JS SDK and CLI landed on July 7, 2026. This is a substantial release bringing a wave of capabilities for developers building headless experiences on Optimizely CMS SaaS — including content type contracts, programmatic application management, a new project scaffolding command, and OpenTelemetry observability.
Alongside these major features, the release addresses critical stability fixes that resolve key edge cases in content fetching, previews, and routing. Below is a deep dive into what has changed, why these updates matter, and how they improve your development workflow.
Content Type Sprawl and the New Contract Enhancements
If you've been building with the SDK for a while, you've probably felt this. Your content model grows. You have a Blog content type, a News Article, a Press Release. They're different types, but they share a bunch of the same properties — category, tags, author, publishedDate. So you define those properties three separate times across three separate contentType() calls. Then a product manager asks you to add a region field to all of them, and you make the same change in three places and hope you didn't miss one.
While you could previously declare shared interfaces, v2.1.0 introduces major enhancements to how the SDK handles content type contracts at runtime to eliminate this duplicate work:
-
Allowed and restricted types on contracts — specify exactly which content types can or must implement a contract
-
Automatic GraphQL fragment generation — when you query a contracted type, the SDK auto-generates the correct inline fragments so all implementing types return their contracted fields without manual query writing
-
Component fallback mechanism — if a content item implements a contract but you haven't registered a specific React component for that particular type, the SDK now falls back to a contract-level component rather than rendering nothing. Silent blank renders caused by missing component registrations were a real source of confusion — this addresses that.
On the CLI side, the pull command now generates and validates contract definitions as part of your code generation step. One sync, and your contracts are in code.
Programmatic Application Management via CLI
The Context: The SDK and CLI provide a highly automated workflow for managing content types in code, but CMS applications (the configuration mapping your frontend hostnames to CMS start pages) historically required manual configuration in the CMS admin UI.
For teams running multiple environments (Development, Staging, Production) or spinning up ephemeral environments for feature branches, this manual step was a deployment bottleneck. If a team member created an environment, they had to manually configure the application in the UI or routing wouldn't resolve. It wasn't version-controlled, automated, or easily reproducible.
The Solution: v2.1.0 introduces full application type definitions and validation to the SDK and exposes three new programmatic management commands in the CLI:
cms-cli createApplication # Create a new CMS application programmatically
cms-cli updateApplication # Update an existing application's configuration
cms-cli checkApplications # Validate registered applications against your local config
**Refer to the official CLI documentation for exact command syntax and options.
Your application configuration can now live in code with the rest of your schema. For teams running automated CI/CD pipelines, this closes a significant orchestration gap.
A Seamless Editing Experience with NextPreviewComponent
The Context:In headless setups, keeping the live preview fast and interactive is critical for content editors. However, standard preview routing setups in Next.js App Router projects often triggered full page reloads upon saving content drafts in the CMS. Editors had to wait for the entire route to re-render and often lost their scroll position or current tab context.
The Solution:v2.1.0 ships the NextPreviewComponent, a preview component specifically optimized for the Next.js App Router rendering lifecycle:
-
Optimized save-event listener — hooks into the content-saved webhook efficiently to detect draft saves immediately without polling
-
Soft refresh support — updates draft content on-screen in place. The editor sees their changes reflect instantly without a full browser reload or losing scroll position
This significantly reduces the friction of the editorial feedback loop, making visual editing in CMS SaaS feel smooth and instant.
Observability Built In: OpenTelemetry Support
The Context: Performance tuning in decoupled architectures is notoriously difficult. If a page load feels laggy, it can be hard to identify whether the delay is happening during client-side hydration, SDK query construction, network latency to Optimizely Graph, or the query processing time within Optimizely Graph. Without deep instrumentation, developers are left guessing or manually wrapping every getContentByPath() call in custom tracing blocks.
The Solution: The SDK now features native OpenTelemetry (OTEL) tracing and metrics. If your hosting environment or application infrastructure runs an OTEL-compatible collector (such as Datadog, New Relic, Honeycomb, Grafana, or Jaeger), the SDK's fetch layer will automatically emit trace spans.
You can now natively profile the duration of your content queries, see exactly how long Optimizely Graph takes to respond, and spot any nested reference fetches that are impacting your latency budget.
Getting Started Faster with create-opti-app
The Context: Historically, starting a new project on CMS SaaS required cloning a complete demo repository, stripping out demo content and styling, configuring environment variables from scratch, and adjusting boilerplates. It lacked a lightweight, standardized onboarding command.
The Solution: The CLI now features a dedicated onboarding and scaffolding utility:
npx @optimizely/cms-cli@latest create-opti-app
This utility lets you quickly scaffold a clean, working project from three officially maintained starter templates:
-
Stride — The modern recommended starter preconfigured for Visual Builder
-
Alloy — The classic Optimizely demonstration site
-
TanStack — TanStack Router + React for projects opting out of Next.js
These templates are maintained alongside the core SDK, ensuring they stay aligned with the latest framework best practices.
Key Stability and Bug Fixes
Behind the major feature updates, v2.1.0 delivers key bug fixes addressing critical routing, caching, and serialization edge cases.
Correcting getContent() Versioning Behavior
The Problem: In certain configurations, the getContent() method (which retrieves a content item by its programmatic ID) was returning the draft version of a page or block to anonymous public visitors rather than the active published version. If an editor had an in-progress draft saved in the CMS, those unapproved changes could unintentionally appear on the live website.
The Fix: The SDK has been corrected to ensure getContent() strictly returns the published version of content for public delivery, safeguarding editorial workflows.
Resolving getContentByPath() 404s on Simple Addresses
The Problem: Optimizely CMS allows editors to assign a "simple address" to any page (e.g., a short URL alias like /about instead of a long hierarchical path). When a page used a simple address, getContentByPath() was throwing a 404 error, breaking routing for pages where custom slugs had been configured.
The Fix: Path-resolution logic has been updated to correctly resolve pages using CMS simple addresses, ensuring routing works smoothly across all customized URLs.
Resolving Empty DAM Asset References in Compositions
The Problem: When assets from Digital Asset Management (DAM) systems were referenced inside Visual Builder compositions (experiences structured using rows, columns, and sections), the returned fields occasionally resolved as null or empty. The query was executing successfully, but the auto-generated GraphQL query was missing the necessary inline fragments to serialize the nested DAM properties.
The Fix: The query generator has been updated to guarantee proper DAM asset fragment resolution inside experience compositions.
Circular Reference Support in Code Generation
The Problem: In complex content models, types frequently reference each other circularly (for example, a Category type containing a parentCategory property of type Category, or a MenuItem pointing to child MenuItems). When the CLI encountered these models, the code generator would enter an infinite loop or fail to compile the output.
The Fix: Both the SDK and CLI have been updated to support circular and self-referential content types by outputting string-based type references at the boundary of a detected cycle, allowing code generation to compile cleanly.
SDK: CMS-53737, PR #409 · CLI: CMS-44353, PR #305
Consistent RichText HTML Entity Decoding
The Problem: Certain encoded HTML entities (such as non-breaking spaces , em dashes –, or custom characters) inside RichText properties were decoded inconsistently, sometimes rendering raw entity markup directly to the page.
The Fix: The entity decoding logic inside the RichText parser has been standardized to ensure consistent rendering across all HTML entities.
Preview Mode: Synchronized Element Focus and Attributes
The Problem: In preview mode, two visual editing issues impacted the editor experience:
-
Selecting an element in the Visual Builder sidebar didn't reliably focus or highlight that corresponding element inside the live iframe.
-
Visual editing attributes (data-epi-* metadata) were missing from custom OptimizelyComposition and OptimizelyGridSection wrappers, rendering some on-page edit boundaries inactive.
The Fix: The preview attribute delivery and sidebar communication have been fully synchronized. On-page edit boundaries now map perfectly to the visual structure, and sidebar element selections correctly shift iframe focus.
Focus: CMS-52749, PR #346 · Attributes: CMS-53437, PR #393
Fallback Resolution for types ending in "...Property"
The Problem: If a custom content type name ended with the suffix "Property" (e.g., HeroImageProperty, the component registry's fallback resolver incorrectly classified it as a system property type rather than a content element, failing to resolve a fallback component.
The Fix: The matching algorithms have been refined to ensure custom content types ending in "Property" correctly evaluate and resolve fallback layouts.
Additional Improvements
|
Change |
Problem |
Solution |
|
Base type properties in queries |
Some inherited base type fields were missing from auto-generated GraphQL queries, requiring manual query extensions |
All base type properties are now automatically included |
|
Locale on ContentType |
No way to specify locale at the type definition level for multilingual setups |
ContentType and RICHTEXT_PRESET now accept an optional locale parameter |
|
Section fragments in compositions |
Sections inside Visual Builder experiences didn't generate proper GraphQL fragments |
Section fragment handling added to OptimizelyComposition |
|
Display template for experiences |
Top-level experience content didn't respect its assigned display template |
Display template resolution now works for top-level experiences |
|
Type re-exports |
Missing re-exports caused broken .d.ts declaration files in consuming projects |
Type re-exports added for proper declaration file generation |
|
Empty contracts config |
Passing an empty contracts array in config threw an error |
Empty contracts are now allowed without errors |
|
Self-signed certificates (CLI) |
CLI couldn't connect to CMS instances behind self-signed certs in dev/staging |
Self-signed certificate support added |
|
RichText editor settings (CLI) |
No way to configure editor settings for RichText properties via the CLI |
Editor settings for RichText are now supported |
|
Duplicate applications (CLI) |
CLI threw errors when multiple applications shared entry points or had missing entry points |
Duplicate handling and missing entry point filtering added |
|
Preview URL with inProcessWebsite (CLI) |
Preview URLs didn't resolve correctly when using inProcessWebsite |
URL resolution logic fixed |
How to Upgrade
# Upgrade the SDK
npm install @optimizely/cms-sdk@2.1.0
# Upgrade the CLI
npm install -D @optimizely/cms-cli@2.1.0
There are no breaking changes in this release. Your existing contentType() schemas, registry configurations, and React component mappings continue to work without modification.
Full Changelog
This post is a community-authored summary based on the official v2.1.0 release notes. For authoritative guidance, always refer to the official Optimizely documentation.
Comments