1.22 release notes
RSSNutrient Web SDK 1.22 removes the blocks UI customization API deprecated in 1.20, leaving the ui slot configuration as the single way to customize the viewer. It also deprecates the collection methods that sit outside the supported API, adds compound mixed-unit measurement labels, an onInitialPageRender callback, typed message IDs and locale tags for localization, and setFocus() support in PDF JavaScript. See the changelog for full details.
The blocks UI customization API is removed
The blocks API deprecated in 1.20 is gone. The ui slot configuration is now the only supported way to customize the viewer’s UI. If you still customize through blocks, your customization no longer applies and the viewer renders its default UI.
ui._blocks and root-level block keys are ignored, and each one logs a console.error naming the key it ignored. Loading isn’t blocked — a stale configuration still opens the document:
// Removed: block-based customization. Logged and ignored.NutrientViewer.load({ ui: { _blocks: { [NutrientViewer.Interfaces.Search]: ({ props }) => ({ content: myCustomSearch(props), }), }, },});
// Use the equivalent slot instead.NutrientViewer.load({ ui: { search: (getInstance, id) => ({ render: () => myCustomSearch(), }), },});Four exports were part of that API and now throw a migration error when accessed, rather than being undefined: NutrientViewer.UI.createBlock, NutrientViewer.UI.Recipes, NutrientViewer.UI.Decorators, and NutrientViewer.UI.Interfaces.
The rendered DOM is unchanged — every data-block-id attribute is preserved — so visual tests and selectors that target them keep working. For the list of slots and what each one replaces, refer to the UI customization and supported slots guides.
Deprecated collection methods
The SDK’s collection types inherit Immutable’s full method surface, most of which the SDK has never supported. Nineteen of those methods are now marked deprecated in the published type declarations and the API reference, each naming the supported replacement. No method was removed and every call keeps working, but the editor now flags them before you build on something that can’t be kept.
| Method | Replacement |
|---|---|
asImmutable | Build the value once, or use withMutations(). |
asMutable | Use withMutations(). |
cacheResult | Drop the call — the collections the SDK exposes are already eager. |
entrySeq | Use entries(). |
filterNot | Use filter() with the predicate negated. |
findEntry | Use entries(), taking the entry you need. |
findKey | Loop over entries(). |
flatten | Use flatMap(). |
fromEntrySeq | Use Map(entries). |
hasIn | Use get() followed by has(). |
mergeIn | Use update() followed by merge(). |
removeIn | Use deleteIn(), or update() followed by delete(). |
setSize | Construct the collection with the values it should hold. |
toIndexedSeq | Use values(). |
toKeyedSeq | Use entries(). |
toMap | Use Map(entries()). |
toSeq | Use entries() on a keyed collection, or values() on an indexed or set-like one. |
toSetSeq | Use values(). |
wasAltered | Compare the collections with equals(). |
Compound measurements
Measurement labels can show a mixed-unit value — “6 ft 3 in” instead of “6.25 ft” — matching the format Acrobat and Bluebeam Revu author. Compound units are configured per scale, as an ordered list of progressively smaller units, each with its own precision, appended to the scale’s primary unit:
NutrientViewer.load({ measurementValueConfiguration: (documentScales) => [ { name: "Feet & Inches", scale: { unitFrom: NutrientViewer.MeasurementScaleUnitFrom.INCHES, unitTo: NutrientViewer.MeasurementScaleUnitTo.FEET, fromValue: 1, toValue: 3 }, precision: NutrientViewer.MeasurementPrecision.TWO, compoundUnits: [ { unitTo: NutrientViewer.MeasurementScaleUnitTo.INCHES, precision: NutrientViewer.MeasurementPrecision.WHOLE } ], selected: true }, ...documentScales ]});The chain is generic across measurement systems — ft → in, m → cm → mm, yd → ft → in — and each annotation renders from its own scale, so a metric scale can’t pick up an imperial sub-unit. A configured sub-unit that isn’t strictly smaller than the scale’s unit falls back to a single-unit label, as do area measurements.
The chain is persisted on the annotation as a multi-entry /Measure /D number-format array, so it survives save, reload, and export, and compound measurements authored elsewhere are read back correctly. instance.setMeasurementScale(scale, { compoundUnits, name }) writes the scale and its chain into the document, so the scale is offered again after reopening without re-supplying measurementValueConfiguration.
Compound measurements are standalone mode only. Document Engine ignores the compound chain. For more information, refer to the measurements guide.
Per-page initial render callback
onInitialPageRender is called once per page, the first time that page renders, which makes time to first rendered page directly measurable instead of something you infer from timers or mutation observers:
const startedAt = performance.now();let reported = false;
NutrientViewer.load({ onInitialPageRender: function ({ pageIndex }) { if (reported) return;
reported = true; console.log(`page ${pageIndex} rendered after ${performance.now() - startedAt}ms`); }, // ...});The callback fires only on a successful render, so the page already has content drawn when you’re called. It never fires twice for the same page index — zoom, rotation, resize, and scrolling back to a page don’t re-trigger it — and the first call can arrive before load() resolves. Pages next to the current one are pre-rendered, so a page can be reported slightly before the reader scrolls to it. The OnInitialPageRenderCallback and InitialPageRenderInfo types ship in the public declarations.
Typed message IDs and locale tags
Localization is now typed. Every message ID the SDK ships and every locale it provides a catalog for is suggested as you type, and the types admit that a locale you haven’t loaded yet isn’t there:
NutrientViewer.I18n.messages.en = { print: "Print document" };
await NutrientViewer.I18n.preloadLocalizationData("de");NutrientViewer.I18n.messages.de!.print = "Dokument drucken";
await instance.setLocale("de");locale, setLocale(), preloadLocalizationData(), and loadTextComparison() autocomplete the shipped locale tags while still accepting a custom tag you push onto NutrientViewer.I18n.locales. The Locale, MessageId, LocaleMessages, and LocaleMessageOverrides types are part of the public API.
This is a type-only tightening: the locale keys of NutrientViewer.I18n.messages are now optional, so reading NutrientViewer.I18n.messages.de.print no longer compiles. Add a guard, or a non-null assertion after preloading. Runtime behavior is unchanged, and JavaScript integrations are unaffected. For the full list of message IDs, refer to the localization guide in the API reference.
setFocus() in PDF JavaScript
Form fields support the setFocus() PDF JavaScript method, so a document script can move the cursor into a named field:
this.getField("total").value = "0";this.getField("total").setFocus();The call previously threw, which discarded the rest of the script — including assignments it had already made. A field on another page is scrolled into view rather than only scrolled to the top of its page.
Immediate saving for DWS Viewer API sessions
A DWS Viewer API session loaded with NutrientViewer.load({ container, session }) runs Instant, but was using the save mode meant for Instant being off. It now uses AutoSaveMode.IMMEDIATE, the mode Instant implies. Edits are sent as they’re made instead of being batched to the next flush point, hasUnsavedChanges() reports accurately, and applying redactions completes instead of hanging indefinitely. Passing instant: false or an explicit autoSaveMode is unaffected.
Inline worker configuration
The inlineWorkers configuration option moved from StandaloneConfiguration to SharedConfiguration, so server-backed deployments can opt out of inline workers as well.
Synthetic text-selection font serialization now runs in a dedicated worker to avoid blocking interaction while scrolling through text-heavy documents. Deployments with a strict Content Security Policy should review the inlineWorkers configuration option.
Document Engine 1.5.6 or later can run this release. In server-backed mode, using SearchType.WORD_BASED, removing password protection during PDF export, and removing annotation notes after an XFDF roundtrip require Document Engine 1.16.0 or later. Document-defined annotation tab order, Annotation.createdBy, overprintPreview, and blackRendering require Document Engine 1.18.0 or later. Compound measurements are standalone mode only and are ignored by Document Engine. See the Web SDK and Document Engine compatibility requirements.
For a complete list of changes, bug fixes, and improvements, refer to the changelog. For previous release notes, refer to the Web SDK 1.21 release notes. We appreciate your feedback and contributions as we continue to enhance Nutrient Web SDK.