- password prompts
- find and replace
- lightweight tools or inspectors
- custom workflow dialogs
dialog— modal, centered, blocks interaction behind itfloating— non-modal, pinned to the visible document area
When to configure surfaces up front
Most apps do not need any extra setup to use surfaces. If you are opening a surface directly withsuperdoc.openSurface(...), you can do that with a normal SuperDoc instance and no special config.
Add modules.surfaces only when you want:
- global defaults for dialogs or floating overlays
- a central
resolverfor intent-based requests usingkind - built-in surface behaviors such as opt-in find/replace or password prompt customization
Open a surface
superdoc.openSurface(request)
Open a dialog or floating surface above the document.
Returns: SurfaceHandle
Surface request
Provide either:
componentorrenderfor a direct surface requestkindfor a resolver-based request
component and render are mutually exclusive.Dialog example
Floating example
Close a surface
superdoc.closeSurface(id?)
Close a surface programmatically.
- pass an
idto close a specific surface - omit
idto close the topmost active surface - if both are open, dialog closes before floating
Handle lifecycle
openSurface() returns a handle with:
idmodeclose(reason?)result
handle.result always resolves. It does not reject for normal lifecycle events.
Possible results:
{ status: 'submitted', data }{ status: 'closed', reason }{ status: 'replaced', replacedBy }{ status: 'destroyed' }
There is one active
dialog slot and one active floating slot. Opening a new surface in the same mode replaces the previous one.Vue component content
For Vue consumers,component is the simplest path.
surfaceIdmoderequestresolve(data?)close(reason?)
React and other frameworks
For React, userender() and mount into the provided container.
Optional instantiation config
Addmodules.surfaces only if you want shared defaults, a resolver, or built-in surface behaviors such as find/replace.
Resolver-based surfaces
Use a resolver when you want to open a surface by intent instead of passing a renderer each time.Built-in Find and Replace
SuperDoc includes a built-in find/replace popover for editor-backed documents. It is disabled by default so existing apps can keep their ownCmd+F / Ctrl+F handling. When enabled, SuperDoc intercepts those shortcuts while focus is inside SuperDoc and opens the built-in UI.
From shortcut handling, SuperDoc only steals Cmd+F / Ctrl+F when it can actually open a surface. If findReplace.resolver returns { type: 'none' }, or the config is invalid or throws, the browser’s native find remains active.
Enable it via modules.surfaces.findReplace:
Text customization
Override any of the built-in labels and placeholders:Find-only mode
Disable replace actions and keep only the find UI:Find/replace configuration. Disabled by default; set to
true to enable the built-in UI, or pass an object to customize labels, disable replace actions, or replace the rendering.Custom Vue component
Replace the built-in content with your own Vue component. Your component receives afindReplace prop with reactive state and actions:
Custom render function (React, vanilla JS)
Userender for framework-agnostic mounting. The render function receives a FindReplaceRenderContext:
ctx exposes container, findReplace, resolve, close, surfaceId, and mode.
ctx.findReplace exposes plain JavaScript getters/setters instead of Vue refs so non-Vue renderers can work with it directly.
Conditional resolver
Useresolver when your app wants to decide at runtime whether to use built-in, custom, external, or suppressed rendering. The resolver receives a read-only context with texts and replaceEnabled:
| Type | Behavior |
|---|---|
null / undefined | Fall through to component/render or built-in |
{ type: 'default' } | Same as null — fall through |
{ type: 'none' } | Suppress SuperDoc’s find/replace surface for this open attempt |
{ type: 'custom', component, props? } | Mount a Vue component in the floating shell |
{ type: 'external', render } | Mount framework-agnostic UI in the floating shell |
component/render. If the resolver returns null or { type: 'default' }, the direct component/render is used. If neither is configured, the built-in popover renders.
Precedence: resolver → component/render → built-in.
The findReplace handle
Custom Vue components receive findReplace as a Vue handle with refs/computed refs. External render functions receive ctx.findReplace with the same API surface, but mutable fields are exposed as plain JavaScript getters/setters and derived fields are plain getters.
| Field | Vue component value | External render value | Description |
|---|---|---|---|
findQuery | Ref<string> | string getter/setter | Current search query |
replaceText | Ref<string> | string getter/setter | Current replacement text |
caseSensitive | Ref<boolean> | boolean getter/setter | Match-case toggle |
ignoreDiacritics | Ref<boolean> | boolean getter/setter | Ignore-diacritics toggle |
showReplace | Ref<boolean> | boolean getter/setter | Whether the replace row is expanded |
matchCount | Ref<number> | number getter | Total match count |
activeMatchIndex | Ref<number> | number getter | Active match index, -1 when none |
matchLabel | ComputedRef<string> | string getter | Formatted label such as 3 of 12 or No results |
hasMatches | ComputedRef<boolean> | boolean getter | Whether there are any matches |
replaceEnabled | boolean | boolean | Whether replace actions are available |
texts | ResolvedFindReplaceTexts | ResolvedFindReplaceTexts | All text strings resolved with defaults |
goNext / goPrev | () => void | () => void | Navigate through matches |
replaceCurrent / replaceAll | () => void | () => void | Run replacement actions |
registerFocusFn | (fn) => void | (fn) => void | Register the function SuperDoc calls when the user presses Cmd+F / Ctrl+F again while the surface is already open |
close | (reason?: unknown) => void | (reason?: unknown) => void | Close the surface |
{ type: 'none' } semantics
{ type: 'none' } means suppress SuperDoc’s find/replace surface for that open attempt.
When find/replace is opened via Cmd+F / Ctrl+F, suppression falls back to the browser’s native find dialog instead of swallowing the shortcut.
Built-in password prompt
SuperDoc includes a built-in password dialog for encrypted DOCX files. Enabled by default. On wrong password, the dialog stays open and shows an error. On success, the document loads normally.false to disable:
Text customization
Override any of the built-in copy:Password prompt configuration. Enabled by default when omitted; set to
false to disable.Custom Vue component
Replace the built-in dialog content with your own Vue component. Your component receives apasswordPrompt prop with everything it needs:
Custom render function (React, vanilla JS)
Userender for framework-agnostic mounting. The render function receives a PasswordPromptRenderContext:
Conditional resolver
Useresolver when you need per-document decisions. The resolver receives a read-only PasswordPromptContext (documentId, errorCode, texts) and returns a resolution:
| Type | Behavior |
|---|---|
null / undefined | Fall through to component/render or built-in |
{ type: 'default' } | Same as null — fall through |
{ type: 'none' } | Suppress the password prompt for this document |
{ type: 'custom', component, props? } | Mount a Vue component in the dialog shell |
{ type: 'external', render } | Mount framework-agnostic UI in the dialog shell |
component/render. If the resolver returns null or { type: 'default' }, the direct component/render is used. If neither is configured, the built-in prompt renders.
Precedence: resolver → component/render → built-in.
The passwordPrompt handle
Every custom UI (component or render) receives a passwordPrompt handle with this shape:
| Field | Type | Description |
|---|---|---|
documentId | string | The document ID requiring a password |
errorCode | string | 'DOCX_PASSWORD_REQUIRED' or 'DOCX_PASSWORD_INVALID' |
texts | ResolvedPasswordPromptTexts | All 11 text strings resolved with defaults |
attemptPassword | (password: string) => Promise<{ success, errorCode? }> | Submit a password attempt |
How actions work
Submit / unlock button:- Call
await passwordPrompt.attemptPassword(password) - If
{ success: true }— callresolve()to close the dialog - If
{ success: false, errorCode }— keep the dialog open, show your error state
resolve() with no arguments is sufficient. The password flow does not consume the resolved data.
Cancel / dismiss button:
- Call
close('user-cancelled')
{ type: 'none' } semantics
{ type: 'none' } means suppress SuperDoc’s password prompt. The resolver context does not expose attemptPassword, so none cannot be used to build a self-hosted modal that retries passwords through SuperDoc.
Use this when your app handles passwords entirely outside SuperDoc — for example, pre-prompting before instantiation or server-side decryption. For global suppression, use passwordPrompt: false instead.
Relationship with the exception event
IfpasswordPrompt is disabled, encrypted files emit DOCX_PASSWORD_REQUIRED or DOCX_PASSWORD_INVALID on the exception event. Your app can handle password entry through that event instead.
When passwordPrompt is enabled, recoverable encryption errors are intercepted by the password prompt flow. If the prompt renders successfully (built-in, custom, or external), the exception event is suppressed. If the prompt cannot render (resolver returned { type: 'none' }, invalid config, or resolver threw), the original exception event is re-emitted so your app can handle it.
Multi-document handling
When multiple encrypted documents are loaded, the password prompt queues one dialog at a time in FIFO order. After the user submits or cancels for one document, the next dialog appears.Styling
Background, shadow, border radius, padding, and edge offset are all customizable via--sd-ui-surface-* and --sd-ui-floating-* CSS variables. Find/replace controls use --sd-ui-find-replace-*, and search highlight colors use --sd-ui-search-match-bg plus --sd-ui-search-match-active-bg. See the full token table in Custom themes.
