Example
Full replacement - custom comment thread with lifecycle:
NutrientViewer.load({
ui: {
commentThread: (getInstance, id) => {
const root = document.createElement("div");
return {
render: (params) => {
root.innerText = `Custom comment thread: ${id}`;
return root;
},
onMount: (id) => console.log("Mounted", id),
onUnmount: (id) => console.log("Unmounted", id),
};
},
},
});
Sub-slot customization - add a banner above the search panel:
NutrientViewer.load({
ui: {
search: {
header: (getInstance, id) => ({
render: () => {
const div = document.createElement("div");
div.textContent = "Custom search header";
return div;
},
}),
},
},
});
Hide a slot entirely:
NutrientViewer.load({
ui: {
tools: {
main: (getInstance, id) => ({ render: () => null }),
},
},
});
Preset - hide everything, then selectively restore:
NutrientViewer.load({
ui: {
preset: "minimal", // hides all UI
search: (getInstance, id) => ({
render: () => {
const div = document.createElement("div");
div.textContent = "Only search is visible";
return div;
},
}),
},
});
Runtime update - switch between custom and default UI:
// Apply custom UI at runtime:
instance.setUI({
annotations: {
actions: (getInstance, id) => ({
render: () => {
const div = document.createElement("div");
div.textContent = "Custom annotation actions";
return div;
},
}),
},
});
// Reset to default:
instance.setUI({});
See Also
UI.Configurationfor the full configuration interface.
UI.SlotConfigurationfor the render/lifecycle contract.
UI.BaseSlotfor the union type every slot accepts.
Interfaces
Type Aliases
AnnotationSlotsBaseSlotCommentThreadUICommentUIContentEditorSlotsDialogAutoResolveDialogSlotDialogSlotConfigurationDialogSlotConfigurationCallbackDialogSlotUIOptionsDocumentComparisonSlotsDocumentEditorUIElectronicSignaturesUIFormCreatorSlotsInstanceGetterMeasurementSlotsSidebarUISignaturePickerUISignatureSlotsSlotConfigurationSlotConfigurationCallbackStampCreationUIStampListUIStampSlotsToolsSlots
UI Namespace - types for the slot-based UI customization API.
Overview
Every visible piece of the SDK UI is a slot - a named placeholder you can fully replace with your own DOM, partially customize (header/body/footer sub-slots), or hide entirely.
Slots are organized by feature domain in UI.Configuration:
main(always-visible),contextual(mode-specific)actions,status,textMarkupInline,deleteConfirm,link,noteexitConfirm,downloadConfirm,cannotSavePrompt,fontMismatch,subsetFontcreate,list,digitalSigning,digitalStatuscreate,listcalibration,settingscontrols,viewpropertyEditorcontainer, plus dynamic custom sidebar panelscommentThread,search,documentEditor,attachmentPreview,passwordPrompt,reloadConfirm,aiAssistantloader(applies to all backend modes)Customization modes
Every slot accepts one of two forms:
(getInstance, id) => SlotConfigurationthat returnsrender,onMount, andonUnmount. The SDK renders whatever DOM noderenderreturns.{ header?, body?, footer? }where each region is its own callback. Regions you omit keep their default rendering.To hide a slot entirely, return
nullfromrender:Lifecycle
The slot callback
(getInstance, id, helpers) => { render, onMount?, onUnmount? }is called once when the SDK prepares to render the component — treat its body as the slot's initialization phase. Some slots, likeloaderandpasswordPrompt, can render beforeload()resolves, sogetInstance()returnsnulluntil the instance is available.render(params)is called on every re-render.For most slots,
onMount(id)fires once the SDK instance is available, sogetInstance()is guaranteed non-null inside it — making it the right place to subscribe to instance events.onUnmount(id)fires once on teardown and is guaranteed to run even when the instance never arrived andonMountwas skipped, so setup done in the callback body (timers, listeners) always has a cleanup path; guard instance-dependent cleanup, asgetInstance()may still benull.loaderandpasswordPromptkeep raw mount timing because they can mount and unmount entirely before an instance exists;getInstance()may returnnullin their lifecycle hooks. Lifecycle hooks are not a place to query the DOM node returned fromrender, because that node may not be inserted yet.Reactive re-rendering
renderis invoked once at mount, then again automatically when the SDK instance becomes available. To re-invoke it on additional changes, callhelpers.requestUpdate()from async work, event handlers, lifecycle hooks, or other imperative code (but not synchronously from withinrender).