Custom Themes
Style the viewer to match your app
The viewer ships with a neutral, Bootstrap-based look served from the embedded resources (viewer.css, plus per-feature stylesheets like searchBar.css and annotationBar.css). Theming happens at three levels — pick the shallowest one that gets you there.
Level 1 — Configure the chrome
Plenty of "theming" is really configuration:
ViewerConfig.ShowThumbs = false— no thumbnail panel.ViewerConfig.FitType = "height" | "width"— how pages fill the viewport.ViewerConfig.ShowThumbs = falsetogether with selectiveCssConfig/ScriptConfigbundles — keep only the chrome your host uses.
Level 2 — Override the CSS
Viewer.ReferenceCss() emits plain <link> tags; anything you load after them wins the cascade. Size and frame the viewer from your own stylesheet:
/* Your stylesheet, loaded after ReferenceCss() */
/* The two containers you own */
#divDocViewer { width: 100%; height: calc(100vh - 48px); }
/* Widget page cards */
.docPage { max-width: none; }The host page controls the outer layout completely — the widget renders inside whatever box you give #divDocViewer / #div_ctlDoc (the standard container pair from the Quick Start).
Two practical rules:
- Don't double-load Bootstrap. If your app already ships Bootstrap, skip
IncludeBootstrapCss/IncludeBootstrapand let the widget inherit yours — this alone makes the viewer look native to your app. - Include only the bundles you use. Every
CssConfig/ScriptConfigflag is opt-in; fewer bundles, fewer styles to fight.
Level 3 — Build your own toolbar
For a fully branded experience, drive the widget from your own UI — this is exactly how
the reference sample builds its main Viewer toolbar. That toolbar is host markup plus an
app-specific helper; it is not an embedded SDK Ribbon. The docViewer instance returned
by initialization is your API surface, along with your own endpoints:
<div id="toolbar">
<button id="btnOpen">Open</button>
<button id="btnPrev">‹</button>
<button id="btnNext">›</button>
<!-- your design system, your icons -->
</div>
<div id="divDocViewer"><div id="div_ctlDoc"></div></div>
<script>
const objViewer = $('#div_ctlDoc').docViewer({
showThumbs: true,
autoLoad: false,
BasePath: '/',
ResPath: 'doconut-res/images',
onViewerReady: () => { /* enable your buttons */ },
onError: (msg) => console.error('DocViewer:', msg)
});
document.getElementById('btnOpen').addEventListener('click', async () => {
const resp = await fetch('/api/open?fileName=Sample.pdf', { method: 'POST' });
const token = await resp.text();
objViewer.View(token);
});
</script>Search and Annotation are different: their optional Ribbon components are embedded in the
SDK (IncludeSearchBar, IncludeAnnotationBar) and generate their own DOM. You can use
them together with your custom Viewer toolbar or skip them and call the underlying widget
APIs yourself. See the
complete Viewer package in Quick Start
for the complete integration order.
Dark mode and RTL
The widget doesn't ship a dark theme; a Level 2 override targeting your dark-mode class (e.g. .dark #divDocViewer { … }) on backgrounds and borders covers most needs, since the page content itself is rendered imagery. For RTL hosts, the viewer lives happily inside an RTL layout — keep your own toolbar RTL-aware.
What not to do
Don't fork or edit the embedded stylesheets: they ship inside the package and are replaced on every update. Overrides in your own stylesheet survive upgrades; edits to viewer.css don't.
Was this page helpful?