Skip to content

A lightweight, extensible React PDF annotator and viewer built on top of PDF.js. Supporting the editing of existing PDF file annotations, posting comments, replying, submitting annotation data, and loading for further editing. 一个基于 PDF.js 构建的轻量级、可扩展的 React PDF 批注器和查看器 支持编辑现有 PDF 文件批注、发布评论、回复、提交注释数据和加载以进行进一步编辑。

License

Notifications You must be signed in to change notification settings

justpankajdotcom/pdfjs-annotation-extension-for-react

 
 

Repository files navigation

pdfjs-annotation-extension-for-react ⚡️

A lightweight, extensible React PDF annotator and viewer built on top of PDF.js
Supporting the editing of existing PDF file annotations, posting comments, replying, submitting annotation data, and loading for further editing.


English | 简体中文

NPM License

Online Demo

Demo

✨ Features

  • ✍️ Rich annotation system
    • Highlight, drawing, shapes, text notes
    • Signatures (draw / enter / upload)
    • Stamps with editor support
    • Edit native PDF annotations directly
  • 📄 High-fidelity PDF rendering based on PDF.js
  • 🎨 Theme system based on Radix UI Themes
  • 🌍 Internationalization (zh-CN, en-US)
  • 🧩 Highly customizable UI
    • Toolbar / Sidebar / Actions fully overridable
  • 🏢 Enterprise-friendly configuration
    • defaultOptions supports DeepPartial + Deep Merge
  • 💾 Export
    • Export annotations to PDF
    • Export annotations to Excel
  • 🧠 Designed for extensibility
    • Clean context & extension architecture

✍️ Annotation Tools

  1. Rectangle
  2. Circle
  3. Free Hand (grouped if drawn within a short time)
  4. Free Highlight (with auto-correction)
  5. Arrow
  6. Cloud
  7. FreeText
  8. Signature
  9. Stamp (upload custom images)
  10. Text Highlight
  11. Text Strikeout
  12. Text Underline
  13. Text

✍️ Editing existing annotations in PDF files

  1. Square
  2. Circle
  3. Ink
  4. FreeText
  5. Line
  6. Polygon
  7. PolyLine
  8. Text
  9. Highlight
  10. Underline
  11. StrikeOut

📦 Installation

npm install pdfjs-annotation-extension-for-react
or
yarn add pdfjs-annotation-extension-for-react

🚀 Quick Start

1. PDF Annotator

import { PdfAnnotator } from 'pdfjs-annotation-extension-for-react'
import 'pdfjs-annotation-extension-for-react/style'

export default function App() {
  return (
    <PdfAnnotator
      title="PDF Annotator"
      url="https://example.com/sample.pdf"
      user={{ id: 'u1', name: 'Alice' }}
      onSave={(annotations) => {
        console.log('Saved annotations:', annotations)
      }}
    />
  )
}

2. Basic PDF Viewer

import { PdfViewer } from 'pdfjs-annotation-extension-for-react'
import 'pdfjs-annotation-extension-for-react/style'

export default function App() {
  return (
    <PdfViewer
      title="PDF Viewer"
      url="https://example.com/sample.pdf"
      layoutStyle={{ width: '100vw', height: '100vh' }}
    />
  )
}

🧩 Components

Base Props

Name Type Default Description
appearance auto | dark | light auto Dark or Light theme.
theme Radix Theme Color violet Theme color of the viewer UI
title React.ReactNode Page title content; accepts text or custom React nodes
url * string | URL PDF file URL; supports string URLs or URL objects
locale 'zh-CN' | 'en-US' zh-CN Locale used for internationalization
initialScale PdfScale auto Initial zoom level of the PDF viewer
layoutStyle React.CSSProperties { width: '100vw', height: '100vh' } Styles applied to the PDF viewer container
isSidebarCollapsed boolean false Whether the sidebar is collapsed by default
enableRange boolean | 'auto' auto Enables HTTP Range (streaming) loading for PDFs

✍️ PdfAnnotator

An advanced PDF viewer with annotation capabilities.

Props

Name Type Default Description
user User { id: 'null', name: 'unknown' } Current user information
used to identify the annotation author
enableNativeAnnotations boolean false Native annotations embedded in the PDF file
defaultShowAnnotationsSidebar boolean false Show Annotations Sidebar
defaultOptions DeepPartial Default configuration for the annotator;
initialAnnotations IAnnotationStore[] Existing annotations to be rendered during initialization
actions React.ReactNode | React.ComponentType Custom actions area 
onSave (annotations: IAnnotationStore[]) => void Callback triggered when annotations are saved
onLoad () => void Callback triggered when the PDF and annotator are fully loaded
onAnnotationAdded (annotation: IAnnotationStore) => void Fired when a new annotation is created
onAnnotationDeleted (id: string) => void Fired when an annotation is deleted
onAnnotationSelected (annotation: IAnnotationStore | null, isClick: boolean) => void Fired when an annotation is selected or deselected
onAnnotationUpdated (annotation: IAnnotationStore) => void Fired when an existing annotation is modified

⚙️ defaultOptions (Enterprise Design)

✅ DeepPartial + Deep Merge

defaultOptions is not a full config override.

  • It is defined as DeepPartial<PdfAnnotatorOptions>
  • It will be deep merged with the system default configuration

This ensures:

  • You only override what you need
  • System defaults remain stable
  • Safe for long-term enterprise use

Example

import qiantubifengshouxietiFont from './fonts/qiantubifengshouxieti.ttf';

<PdfAnnotator
    url="sample.pdf"
    defaultOptions={{
        colors: ['#000', '#1677ff'],
        signature: {
            colors: ['#000000', '#ff0000', '#1677ff'],
            type: 'Upload',
            maxSize: 1024 * 1024 * 5,
            accept: '.png,.jpg,.jpeg,.bmp',
            defaultSignature: ['data:image/png;base64,...'],
            defaultFont: [
                {
                    label: '楷体',
                    value: 'STKaiti',
                    external: false
                },
                {
                    label: '千图笔锋手写体',
                    value: 'qiantubifengshouxieti',
                    external: true,
                    url: qiantubifengshouxietiFont
                },
                {
                    label: '平方长安体',
                    value: 'PingFangChangAnTi-2',
                    external: true,
                    url: 'http://server/PingFangChangAnTi-2.ttf'
                }
            ]
        },
        stamp: {
            maxSize: 1024 * 1024 * 5,
            accept: '.png,.jpg,.jpeg,.bmp',
            defaultStamp: ['data:image/png;base64,...'],
            editor: {
                defaultBackgroundColor: '#2f9e44',
                defaultBorderColor: '#2b8a3e',
                defaultBorderStyle: 'none',
                defaultTextColor: '#fff',
                defaultFont: [
                    {
                        label: '楷体',
                        value: 'STKaiti'
                    }
                ]
            }
        }
    }}
/>

🎨 Custom UI

Custom Actions

<PdfAnnotator
  url={pdfUrl}
  actions={({ save, exportToPdf, exportToExcel }) => (
    <>
      <button onClick={save}>Save</button>
      <button onClick={() => exportToPdf('annotations')}>
        Export PDF
      </button>
      <button onClick={() => exportToExcel('annotations')}>
        Export Excel
      </button>
    </>
  )}
/>

🖋 Signature & Stamp Configuration

<PdfAnnotator
  url={pdfUrl}
  defaultOptions={{
    signature: {
      defaultSignature: ['data:image/png;base64,...'],
      defaultFont: [
        {
          label: 'Custom Font',
          value: 'MyFont',
          external: true,
          url: '/fonts/myfont.ttf'
        }
      ]
    },
    stamp: {
      defaultStamp: ['data:image/png;base64,...']
    }
  }}
/>

📄 PdfViewer

A lightweight PDF viewer with toolbar, sidebar, actions and extensible UI slots.

Props

Name Type Default Description
actions React.ReactNode | (context: PdfViewerContextValue) => React.ReactNode Custom actions area in the toolbar
sidebar SidebarPanel[] Custom sidebar component
toolbar React.ReactNode | (context: PdfViewerContextValue) => React.ReactNode Custom toolbar component
showTextLayer boolean true Whether to render the text layer
showAnnotations boolean false Whether to render the pdf annotations layer
defaultActiveSidebarKey string null Default Active Sidebar Key
onDocumentLoaded (pdfViewer: PDFViewer | null) => void Callback invoked when the PDF
document is fully loaded and the viewer is initialized
onEventBusReady (eventBus: EventBus | null) => void Callback invoked when the pdf.js EventBus is ready

🎨 Custom UI

Custom Toolbar

<PdfViewer
  url={pdfUrl}
  toolbar={(context) => (
    <>
    <button onClick={() => console.log(context.pdfViewer)}>
        PDF Viewer
    </button>
    <button onClick={context.toggleSidebar}>
        Toggle Sidebar
    </button>
    <button onClick={() => context.setSidebarCollapsed(false)}>
        Open Sidebar
    </button>
    <button onClick={() => context.setSidebarCollapsed(true)}>
        Close Sidebar
    </button>
</>
  )}
/>

Custom Sidebar

<PdfViewer
  url={pdfUrl}
   sidebar={[{
            key: 'sidebar-1',
            title: 'Sidebar 1',
            icon: <BsLayoutTextSidebar />,
            render: (context) => (
                <div style={{ display: 'flex', gap: 10, flexDirection: 'column' }}>
                    Sidebar 1
                    <button onClick={context.toggleSidebar}>
                        toggleSidebar
                    </button>
                    <button onClick={() => console.log(context.pdfViewer)}>
                        Get PDF Viewer
                    </button>
                    <button onClick={() => {
                        context.pdfViewer?.scrollPageIntoView({
                            pageNumber: 1
                        })
                    }}>
                        goto page1
                    </button>
                    <button onClick={() => {
                        context.pdfViewer?.scrollPageIntoView({
                            pageNumber: 10
                        })
                    }}>
                        goto page 10
                    </button>
                </div>
            )
        }]}
/>

Custom Actions

<PdfViewer
  url={pdfUrl}
  actions={(context) => (
    <>
        <button onClick={() => console.log(context.pdfViewer)}>
            PDF Viewer
        </button>
        <button onClick={context.toggleSidebar}>
            Toggle Sidebar
        </button>
        <button onClick={() => context.setSidebarCollapsed(false)}>
            Open Sidebar
        </button>
        <button onClick={() => context.setSidebarCollapsed(true)}>
            Close Sidebar
        </button>
    </>
  )}
/>

🌍 Browser Support

  • Chrome (latest)
  • Firefox (latest)
  • Safari (latest)
  • Edge (latest)

📄 License

MIT

About

A lightweight, extensible React PDF annotator and viewer built on top of PDF.js. Supporting the editing of existing PDF file annotations, posting comments, replying, submitting annotation data, and loading for further editing. 一个基于 PDF.js 构建的轻量级、可扩展的 React PDF 批注器和查看器 支持编辑现有 PDF 文件批注、发布评论、回复、提交注释数据和加载以进行进一步编辑。

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • TypeScript 97.5%
  • SCSS 2.4%
  • HTML 0.1%