The [dark theme][] guide mentions the different theme presets available in Nutrient Web SDK. Starting with Nutrient Web SDK 1.5, you can build your theme and use it in the Web Viewer. In addition to the presets we already provide, you can now create your own theme.

Minimal theme starter

If you’re just getting started with theming, here’s a minimal example that changes only the primary colors. You’ll still need to provide the complete theme object, but this shows the essential parts to customize:

import NutrientViewer from "@nutrient-sdk/viewer";
// Start with the default light theme and modify specific values.
const minimalCustomTheme = {
// ... full theme object required (see complete example below)
// Key sections to customize for quick branding:
// - color.background.primary: Main background colors
// - color.background.interactive: Button/link colors
// - color.text.primary: Main text color
// - color.border: Border colors
};
const container = document.getElementById("nutrient-container");
NutrientViewer.load({
container,
document: "document.pdf",
theme: minimalCustomTheme,
}).catch((error) => {
console.error("Failed to load viewer:", error);
});

You cannot pass a partial theme object. You must provide the complete theme object, including properties you don’t want to change. Copy the full example below and modify only what you need.

Applying a theme to the entire viewer

To apply a theme to the entire viewer, you need to set the [NutrientViewer.Configuration#theme][theme property] property to your theme object (try the CSS override approach in the Playground(opens in a new tab)):

import NutrientViewer from "@nutrient-sdk/viewer";
const customTheme = {
// ═══════════════════════════════════════════════════════════════════════════
// Elevation — Box shadow values for depth/layering effects
// Used for dropdowns, modals, tooltips, and floating elements
// ═══════════════════════════════════════════════════════════════════════════
elevation: {
low: "0 0 4px 0 rgba(0, 0, 0, 0.4)", // Subtle shadow for slight elevation
medium: "0 4px 16px 0 rgba(0, 0, 0, 0.15)", // Prominent shadow for modals/dropdowns
},
// ═══════════════════════════════════════════════════════════════════════════
// Opacity — Transparency values used throughout the UI
// Applied to disabled states, overlays, and hover effects
// ═══════════════════════════════════════════════════════════════════════════
opacity: {
none: "0", // Fully transparent
low: "0.1", // Barely visible (subtle overlays)
medium: "0.5", // Semi-transparent (disabled states)
high: "0.9", // Nearly opaque (hover overlays)
},
// ═══════════════════════════════════════════════════════════════════════════
// Rounded — Border radius values for rounded corners
// Applied to buttons, inputs, cards, and other UI elements
// ═══════════════════════════════════════════════════════════════════════════
rounded: {
xs: "2px", // Minimal rounding (table cells)
sm: "4px", // Small rounding (buttons, inputs)
md: "8px", // Medium rounding (cards, panels)
lg: "12px", // Large rounding (modals)
xl: "16px", // Extra large rounding
"2xl": "24px", // Very rounded corners
full: "9999px", // Fully rounded (pills, avatars)
},
// ═══════════════════════════════════════════════════════════════════════════
// Spacing — Margin and padding values
// Used for consistent spacing throughout the interface
// ═══════════════════════════════════════════════════════════════════════════
spacing: {
xs: "2px", // Tiny spacing (icon gaps)
sm: "4px", // Small spacing (compact elements)
md: "8px", // Medium spacing (standard padding)
lg: "12px", // Large spacing (section padding)
xl: "16px", // Extra large spacing
"2xl": "24px", // Double extra large
"3xl": "32px", // Section margins
"4xl": "40px", // Large section gaps
"5xl": "48px", // Major section spacing
"6xl": "64px", // Page-level spacing
"7xl": "80px", // Large page margins
"8xl": "96px", // Extra large margins
"9xl": "160px", // Maximum spacing
},
// ═══════════════════════════════════════════════════════════════════════════
// Color — All color definitions for the theme
// This is the most important section for visual customization
// ═══════════════════════════════════════════════════════════════════════════
color: {
// ─────────────────────────────────────────────────────────────────────────
// Support colors — Semantic colors for status messages and alerts
// Used in validation, notifications, and system feedback
// ─────────────────────────────────────────────────────────────────────────
support: {
error: {
subtler: "#711b00", // Darkest error (backgrounds)
subtle: "#b53007", // Dark error (borders)
medium: "#fe7a68", // Standard error (text, icons)
strong: "#ffd4ce", // Light error (highlighted backgrounds)
},
success: {
subtler: "#223a03", // Darkest success
subtle: "#3C6612", // Dark success
medium: "#80CC34", // Standard success
strong: "#ecfeda", // Light success
},
warning: {
subtler: "#562800", // Darkest warning
subtle: "#bd5a00", // Dark warning
medium: "#eb7f00", // Standard warning
strong: "#ffd4a1", // Light warning
},
info: {
subtler: "#190d94", // Darkest info
subtle: "#4537de", // Dark info
medium: "#777cf0", // Standard info
strong: "#d3dcff", // Light info
},
},
// ─────────────────────────────────────────────────────────────────────────
// Focused — Colors for focus rings and accessibility indicators
// Critical for keyboard navigation and accessibility
// ─────────────────────────────────────────────────────────────────────────
focused: {
default: "#FB6F3B", // Main focus ring color
inset: "#5B1C0F", // Inset focus indicator
},
// ─────────────────────────────────────────────────────────────────────────
// Background — All background colors
// Controls the visual appearance of surfaces and containers
// ─────────────────────────────────────────────────────────────────────────
background: {
// Primary backgrounds — Main viewer surfaces
primary: {
subtle: "#5B1C0F", // Lightest primary background
medium: "#431207", // Standard primary background
strong: "#180501", // Darkest primary background (main canvas)
},
// Interactive backgrounds — Buttons, links, clickable elements
interactive: {
enabled: "#FB6F3B", // Default button background
hovered: "#FEB609", // Hovered button background
active: "#FFC938", // Active/pressed button background
visited: "#FFC938", // Visited link background
disabled: "rgb(84.7% 28.6% 16.1% / 0.5)", // Disabled state
},
// Inverse backgrounds — For contrast areas
inverse: {
subtle: "#E95635",
medium: "#FFC938",
strong: "#FFE7A9",
},
// Secondary backgrounds — Sidebars, panels, cards
secondary: {
subtle: "#762B1A",
medium: "#A93920",
strong: "#D84929",
},
// Overlay backgrounds — Modals, dropdowns, tooltips
overlay: {
subtle: "rgb(100% 90.6% 66.3% / 0.25)", // Light overlay
medium: "rgb(9.41% 1.96% 0.39% / 0.5)", // Medium overlay
interactive: "rgb(98.4% 43.5% 23.1% / 0.25)", // Interactive overlay
},
// Positive backgrounds — Selection, highlights
positive: {
subtle: "#FFC938",
medium: "#FEDE89",
strong: "#FFE7A9",
interactive: {
enabled: "#5B1C0F",
},
},
},
// ─────────────────────────────────────────────────────────────────────────
// Text — All text colors
// Controls readability and visual hierarchy of content
// ─────────────────────────────────────────────────────────────────────────
text: {
primary: "#FFE7A9", // Main body text
secondary: "#FEB609", // Secondary/muted text
helper: "#FB6F3B", // Helper text, captions
placeholder: "#FB6F3B", // Input placeholder text
inverse: "#5B1C0F", // Text on light backgrounds
oninteractive: "#5B1C0F", // Text on interactive elements (buttons)
interactive: {
enabled: "#FB6F3B", // Link text
hovered: "#FEB609", // Hovered link text
active: "#FFC938", // Active link text
visited: "#FFC938", // Visited link text
disabled: "rgb(100% 90.6% 66.3% / 0.5)", // Disabled text
},
},
// ─────────────────────────────────────────────────────────────────────────
// Icon — All icon colors
// Usually matches text colors but can be customized separately
// ─────────────────────────────────────────────────────────────────────────
icon: {
primary: "#FFE7A9", // Main icon color
secondary: "#FB6F3B", // Secondary icon color
inverse: "#5B1C0F", // Icons on light backgrounds
oninteractive: "#5B1C0F", // Icons on buttons
interactive: {
enabled: "#FB6F3B", // Interactive icon
hovered: "#FEB609", // Hovered icon
active: "#FFC938", // Active icon
visited: "#FFC938", // Visited state
disabled: "rgb(100% 90.6% 66.3% / 0.5)", // Disabled icon
},
},
// ─────────────────────────────────────────────────────────────────────────
// Border — All border colors
// Defines boundaries between UI elements
// ─────────────────────────────────────────────────────────────────────────
border: {
subtle: "#762B1A", // Subtle borders (dividers)
medium: "#A93920", // Standard borders
strong: "#D84929", // Prominent borders
inverse: "#FFE7A9", // Borders on dark backgrounds
interactive: {
enabled: "#FB6F3B", // Interactive element borders
hovered: "#FEB609", // Hovered borders
active: "#FFC938", // Active borders
visited: "#FFC938", // Visited state
disabled: "#D84929", // Disabled borders
},
positive: {
interactive: {
enabled: "#5B1C0F",
},
subtle: "#FFC938",
medium: "#FEB609",
strong: "#FB6F3B",
},
},
},
// ═══════════════════════════════════════════════════════════════════════════
// Typography — Font definitions for all text styles
// Controls font family, size, weight, and line height
// Format: 'weight size/line-height font-family'
// ═══════════════════════════════════════════════════════════════════════════
typography: {
// ─────────────────────────────────────────────────────────────────────────
// Headings — Title text styles (h1–h6)
// ─────────────────────────────────────────────────────────────────────────
heading: {
h6: {
regular: {
font: '400 0.875rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
medium: {
font: '500 0.875rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
semibold: {
font: '600 0.875rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
},
h5: {
regular: {
font: '400 1rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
medium: {
font: '500 1rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
semibold: {
font: '600 1rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
},
h4: {
regular: {
font: '400 1.375rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0",
},
medium: {
font: '500 1.375rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0",
},
semibold: {
font: '600 1.375rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0",
},
},
h3: {
regular: {
font: '400 1.5rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0",
},
medium: {
font: '500 1.5rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0",
},
semibold: {
font: '600 1.5rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0",
},
},
h2: {
regular: {
font: '400 1.75rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0",
},
medium: {
font: '500 1.75rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0",
},
semibold: {
font: '600 1.75rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0",
},
},
h1: {
regular: {
font: '400 2rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0",
},
medium: {
font: '500 2rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0",
},
semibold: {
font: '600 2rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0",
},
},
},
// ─────────────────────────────────────────────────────────────────────────
// Labels — UI labels, form labels, button text
// ─────────────────────────────────────────────────────────────────────────
label: {
sm: {
regular: {
font: '400 0.688rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.05px",
},
medium: {
font: '500 0.688rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.05px",
},
semibold: {
font: '600 0.688rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.05px",
},
},
md: {
regular: {
font: '400 0.75rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
medium: {
font: '500 0.75rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
semibold: {
font: '600 0.75rem/1.25 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
},
lg: {
regular: {
font: '400 0.875rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
medium: {
font: '500 0.875rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
semibold: {
font: '600 0.875rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
},
},
// ─────────────────────────────────────────────────────────────────────────
// Body — Body text, paragraphs, descriptions
// ─────────────────────────────────────────────────────────────────────────
body: {
sm: {
regular: {
font: '400 0.75rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.05px",
},
medium: {
font: '500 0.75rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.05px",
},
semibold: {
font: '600 0.75rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.05px",
},
},
md: {
regular: {
font: '400 0.875rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
medium: {
font: '500 0.875rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
semibold: {
font: '600 0.875rem/1.375 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
},
lg: {
regular: {
font: '400 1rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
medium: {
font: '500 1rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
semibold: {
font: '600 1rem/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif',
letterSpacing: "0.1px",
},
},
},
},
};
// Get the container element where the viewer will be mounted.
const container = document.getElementById("nutrient-container");
NutrientViewer.load({
container,
document: "document.pdf",
theme: customTheme,
}).catch((error) => {
console.error("Failed to load viewer with custom theme:", error);
});

You can find the complete signature of the theme object in the API documentation. One thing to keep in mind is that you cannot pass a partial theme object. You need to pass the whole theme object, including the properties you don’t want to override. You can find an example of a theme object here.

Applying theme to a specific section

The JSON object above is applied to the whole viewer in form of CSS variables. This enables you to override the theme for a specific section of the viewer by just overriding the CSS variables for that section. These variables start with the prefix --bui-. Every theme object property is mapped to a CSS variable. For example, the color.background.primary.strong property is mapped to the --bui-color-background-primary-strong CSS variable.

For example, if you want to override the error support color for the toolbar, you can do the following:

.PSPDFKit-Toolbar {
--bui-color-support-error-subtler: #000;
--bui-color-support-error-subtle: #000;
--bui-color-support-error-medium: #000;
--bui-color-support-error-strong: #000;
}

You can find the complete list of CSS variables in the API documentation.