Guides

Theming and customization

The SDK renders its UI unstyled on purpose: your application's design system supplies the look. All visual customization goes through the theme configuration of PdfProviders (memoized like any other configuration, see Getting started):

OptionTypeWhat it does
viewportPaddingnumber | number[]The gap between the pages and the viewport edges, in pixels. Follows the CSS padding shorthand.
classNamesRecord<StyleKeys, string>Class names appended to the elements the SDK renders.
stylesRecord<StyleKeys, CSSProperties>Inline styles applied to the elements the SDK renders.
components{ pageOverlay, widgetOverlay, widgetPreview }Your components, replacing built-in ones or added to every page. See Custom components.

Styleable elements

classNames and styles share the same keys, one per element the SDK renders:

KeyElement
scrollViewportThe viewport: the scrollable, zoomable area holding the pages.
pagesContainerThe element laying out the pages inside the viewport.
pageOne page.
pageCanvasThe canvas rendering a page's content.
rubberbandSelectionThe rectangle drawn while drag-selecting blocks.
resizableThe frame around a selected block.
resizableHandleA resize handle on that frame.
widgetOverlayThe interactive overlay over a form widget.
widgetInputThe input element inside a text or choice widget.
widgetSignatureThe content of a signature widget.
widgetSignatureHintThe hint inside an empty signature widget.
widgetPreviewThe preview box while placing a new widget.
imagePreviewThe preview shown while placing an image.
searchMatchA text match highlighted by document search.

The searchMatch element is part of the text layout, so style it with paint properties only, such as background or color: a padding, margin, or border moves the text. Give lengths units ('4px', not 4). The active match carries a data-active attribute, so a classNames.searchMatch class can style it apart with a [data-active] selector.

CSS variables

The viewport element defines CSS variables that the selection and widget visuals read. Override them through styles.scrollViewport, or in CSS through your classNames.scrollViewport class:

VariableControls
--selection-colorThe selection outlines, handles, and the rubberband.
--selection-handle-fill-colorThe fill of the resize handles.
--outline-widthThe width of the selection outline.
--outline-offsetThe offset of the selection outline from the block.
--widget-overlay-backgroundThe background of the interactive widget overlay.
--widget-focused-overlay-backgroundThe background of the focused widget's overlay.
--search-match-backgroundThe highlight of a search match.
--search-match-active-backgroundThe highlight of the active search match.

There are also --outline-touch-drag-* and --touch-indicator-* variables tuning the outline and the touch indicator during touch interactions.

Putting it together

import { Config, PdfProviders } from '@sdk-pdf/react'

const theme: Config['theme'] = {
  viewportPadding: [24, 16],
  classNames: { scrollViewport: 'my-viewport', page: 'my-page' },
  styles: {
    scrollViewport: { '--selection-color': 'rebeccapurple' } as React.CSSProperties,
    resizableHandle: { borderRadius: '50%' },
  },
}

export function App() {
  return (
    <PdfProviders theme={theme}>
      <DocumentLoader />
    </PdfProviders>
  )
}

Custom components

theme.components replaces parts of the SDK UI with your own components:

KeyWhat it does
widgetOverlayReplaces the highlight drawn over a form widget. Receives the props of WidgetOverlay.
widgetPreviewReplaces the box following the cursor while the addWidget interaction tool is active. Receives the props of WidgetPreview.
pageOverlayAdds your component on top of every visible page, covering it. Receives the page id, its index, and a class name that positions it over the page.

Each value is a component or a React element. A replacement receives the same props as the built-in component, including the className and style from classNames and styles. The built-in components are exported, so a replacement can wrap them instead of starting from scratch:

import { Config, PageOverlayProps, WidgetOverlay, useWidgetById } from '@sdk-pdf/react'
import { ComponentProps } from 'react'

// Marks the required form fields while keeping the built-in highlight.
function RequiredFieldOverlay(props: ComponentProps<typeof WidgetOverlay>) {
  const widget = useWidgetById(props.widgetId)

  return <WidgetOverlay {...props} data-required={widget?.isRequired || undefined} />
}

// Shows the page number over each page.
function PageNumberOverlay({ pageIndex, className }: PageOverlayProps) {
  return (
    <div className={className}>
      <span className="page-number">{pageIndex + 1}</span>
    </div>
  )
}

const theme: Config['theme'] = {
  viewportPadding: 24,
  components: { widgetOverlay: RequiredFieldOverlay, pageOverlay: PageNumberOverlay },
}

Like the rest of the configuration, keep the theme object stable between renders: define it outside the component or memoize it.

On this page