@zerohive/hive-viewer
v2.0.4
Published
`@zerohive/hive-viewer` is a browser-first React document viewer with signing, annotations, save, and export workflows.
Readme
@zerohive/hive-viewer
@zerohive/hive-viewer is a browser-first React document viewer with signing, annotations, save, and export workflows.
It is designed for product teams that need to:
- open a document from a URL,
base64, orBlob - let users review it in-app
- place signatures and annotations on the document surface
- save or export the result
- persist the returned file and metadata in their own backend
Install
npm install @zerohive/hive-viewerImport the stylesheet once in your app:
import "@zerohive/hive-viewer/styles.css";Next.js Usage
The viewer uses browser APIs, so in Next.js it should be rendered client-side.
"use client";
import dynamic from "next/dynamic";
import "@zerohive/hive-viewer/styles.css";
const DocumentViewer = dynamic(
async () => (await import("@zerohive/hive-viewer")).DocumentViewer,
{ ssr: false },
);
export default function Page() {
return (
<DocumentViewer
mode="view"
fileUrl="https://example.com/contracts/master-service-agreement.pdf"
fileName="master-service-agreement.pdf"
fileType="pdf"
/>
);
}What The Package Does
At a high level, the package works like this:
- You pass a document source into
DocumentViewer. - The viewer picks the right renderer for the file type.
- Users can navigate, zoom, sign, and annotate.
- Signature placements and annotations are tracked as structured JSON metadata.
- When the user saves, the package returns:
- the saved file as
base64 - metadata describing the saved file
- the signature placements and annotations used in the review
- the saved file as
That makes the package useful for both:
- final file generation
- restoring a review session later
Supported Sources
You can load a document using one of these props:
fileUrlbase64blob
You should also provide:
fileNamefileType
Supported File Types
Best-supported document types:
pdfdocxmdtxtxlsxcsvpptxpngjpgjpeggifbmpsvg
Accepted legacy formats with more limited fidelity:
docrtfxlsppt
Viewer Modes
DocumentViewer supports three modes:
viewFor document review and signing.editFor editable text and spreadsheet-style workflows where supported.createFor building a new document session from the chosenfileType.
Mode Support By Format
The package now applies mode support honestly by file type.
Full Authoring Support
These formats support view, edit, and create:
docxdocrtftxtmdxlsxxlscsv
Review-Only Formats
These formats are currently best used for view, signing, annotations, save, and export:
pdfpptxpptpngjpgjpeggifbmpsvgxml
If a consumer requests edit or create for a review-only format, the viewer now falls back cleanly:
- unsupported
editbecomesviewwith a clear notice - unsupported
createwith a source document becomesviewwith a clear notice - unsupported
createwithout a source shows a capability message instead of a broken blank editor
Rich Text Authoring
For text-style documents (docx, doc, rtf, txt, md), edit and create now provide a fuller authoring surface:
- a style picker for paragraph, headings, and quote blocks
- formatting controls for bold, italic, underline, bullets, numbering, and alignment
- insert actions for links, dividers, and simple tables
- undo/redo and clear-formatting actions
- create-mode starter templates such as blank document, letter, memo, meeting notes, agreement, and proposal
This makes the package much better for lightweight in-app document drafting, review preparation, and internal document generation.
Current rich-text authoring also includes:
- image insertion and upload
- image sizing, crop presets, alignment, zoom, and focal-point editing
- table row and column controls when the cursor is inside a table
- a hideable create-mode template strip
- starter templates for blank, letter, meeting notes, agreement, and proposal
Finalize-To-PDF Workflow
For products that only need a final signed document for viewing, sharing, or archiving, the package can finalize signed sessions to PDF.
- when
finalizeSignedDocumentsAsPdfis enabled, signed or annotated save actions default to PDF onSavereturns the finalized PDF asbase64- the save metadata still includes signature placements, annotations, and signature summaries
This is the recommended path when your backend stores a final artifact in object storage and returns a file URL for later viewing.
Letterhead Support
The package supports structured finalized-PDF letterheads through letterheadTemplate.
You can pass it as a prop without saving it anywhere, or generate it from user/company settings in your host app.
- header logo, brand name, subtitle, badge, colors, and divider styling
- footer title, lines, alignment, and divider styling
- same-origin image URLs and SVG logos are supported best
This is separate from headerComponent and footerComponent: the React components can be used for viewer-side preview, while letterheadTemplate drives the finalized PDF letterhead layout.
Host-Provided Signatures
If your product already stores user signatures, use onSignRequest.
Typical flow:
- User clicks
Sign Document. - Your app opens a PIN or approval dialog.
- Your backend verifies the PIN and returns the signature image plus signer details.
onSignRequestreturns that signature object to the package.- The package places it on the document and includes it in save metadata.
The package supports:
- URL or base64 signature images
- optional
signedBy - optional
jobTitle - date normalization to
dd-mm-yyyy - per-placement signature colors:
black,blue,red,green
Basic Example
"use client";
import { useState } from "react";
import {
DocumentViewer,
type AnnotationPlacement,
type DocumentViewerSaveMeta,
type Signature,
type SignaturePlacement,
} from "@zerohive/hive-viewer";
import "@zerohive/hive-viewer/styles.css";
export default function ContractReview() {
const [signatures, setSignatures] = useState<Signature[]>([]);
const [signaturePlacements, setSignaturePlacements] = useState<
SignaturePlacement[]
>([]);
const [annotations, setAnnotations] = useState<AnnotationPlacement[]>([]);
return (
<DocumentViewer
mode="view"
fileUrl="https://example.com/contracts/msa.pdf"
fileName="msa.pdf"
fileType="pdf"
allowSigning
allowAnnotations
signatures={signatures}
signaturePlacements={signaturePlacements}
annotations={annotations}
onSignRequest={async () => {
const signature = {
id: crypto.randomUUID(),
signatureImageUrl: "data:image/png;base64,...",
signedBy: "Jane Doe",
dateSigned: new Date().toISOString(),
};
setSignatures((prev) => [...prev, signature]);
return signature;
}}
onSignaturePlacementsChange={setSignaturePlacements}
onAnnotationsChange={setAnnotations}
onSave={async (editedFileAsBase64, meta) => {
await saveToBackend(editedFileAsBase64, meta);
}}
/>
);
}
async function saveToBackend(
editedFileAsBase64: string,
meta: DocumentViewerSaveMeta,
) {
await fetch("/api/documents/save", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
fileBase64: editedFileAsBase64,
fileName: meta.fileName,
fileType: meta.fileType,
exportedAsPdf: meta.exportedAsPdf ?? false,
signaturePlacements: meta.signaturePlacements ?? [],
annotations: meta.annotations ?? [],
}),
});
}Signing And Annotation Model
The package treats placed signatures and annotations as first-class review data.
Signatures
A Signature is the reusable signature asset itself:
id?signatureImageUrlsignedBy?jobTitle?dateSigned
Notes:
- package-managed visible dates are normalized to
dd-mm-yyyy - the signature asset stays reusable, while color is applied at placement level
A placed signature is represented as a SignaturePlacement:
idsignatureId?signaturesignatureColor?surfaceKindsurfaceKeypage?slide?sheetName?xywidthheight
Annotations
Annotations are separate from signatures and can exist:
- on their own
- linked to a placed signature
An AnnotationPlacement includes:
idsurfaceKindsurfaceKeypage?slide?sheetName?xywidthheighttextlinkedSignaturePlacementId?linkedSignatureId?
All placement geometry is stored in normalized coordinates, so overlays can be restored across zoom and layout changes.
What Gets Returned On Save
The main save contract is:
onSave?: (editedFileAsBase64: string, meta: DocumentViewerSaveMeta) => void;editedFileAsBase64
- the saved/exported file contents
- ready to send to your backend or upload to object storage
meta
fileNamefileTypeexportedAsPdf?signaturePlacements?annotations?signatures?signatureList?
The important part for consumers is that the package returns both:
- the file
- the structured overlay metadata
That means the consumer can store:
- the uploaded file URL returned by their backend or bucket
- the placements and annotations JSON for reopening later
Recommended Backend Wiring
Most products wire the package like this:
- User reviews document in
DocumentViewer. - User clicks
SaveorExport as PDF. - The package calls
onSave(base64, meta). - Your app sends that
base64to the backend. - Your backend uploads the file to storage.
- Your backend returns a stored file URL.
- Your app saves that URL together with
signaturePlacementsandannotations.
Example Payload Sent To Backend
{
"fileBase64": "<base64 returned by onSave>",
"fileName": "contract.docx",
"fileType": "docx",
"exportedAsPdf": false,
"signaturePlacements": [],
"annotations": []
}Example Record Stored In Your Database
{
"documentId": "doc_123",
"fileUrl": "https://bucket.example.com/contracts/doc_123.docx",
"fileName": "contract.docx",
"fileType": "docx",
"exportedAsPdf": false,
"signaturePlacements": [],
"annotations": []
}Reopening A Saved Document
When you want to show the document again in your app, pass the saved file plus the saved overlay metadata back into the viewer:
<DocumentViewer
mode="view"
fileUrl={record.fileUrl}
fileName={record.fileName}
fileType={record.fileType}
signaturePlacements={record.signaturePlacements}
annotations={record.annotations}
/>This is the key integration idea:
- the file gives the viewer the document source
signaturePlacementsandannotationsrestore the review layer
Live Review State Callbacks
If you want autosave before the user clicks Save, you can listen to:
onSignaturePlacementsChangeonAnnotationsChange
These callbacks are useful for draft persistence, collaborative review state, or saving progress during long sessions.
Save Behavior By Format
The package can return different output formats depending on the source file type and the user action.
Save
pdfsaves aspdfpptxandpptsave as nativepptxxlsx,xls, andcsvsave as nativexlsxdocx,doc,rtf,txt, andmdsave as a visualdocx- JPEG sources save as
jpg - other image sources save as
png
Export as PDF
- returns a PDF output
meta.exportedAsPdfistruemeta.fileTypewill bepdf
Consumers should rely on the returned meta.fileType, not the original input file type.
Important Persistence Note
There are two different ways consumers may reopen a document:
1. Reopen for continued review
Use:
- the saved source document URL or
base64 signaturePlacementsannotations
This is the normal review-session restore pattern.
2. Reopen the final baked file
Use:
- the final file returned by
onSave
If your saved file already visually includes signatures and annotations, and you also pass the same signaturePlacements and annotations back into the viewer, the user may see them twice.
For that reason, many apps keep:
- a source/review version
- a final exported artifact
Core Props
Commonly used props:
modefileUrlbase64blobfileNamefileTypeallowSigningdisableSigningallowAnnotationsdisableAnnotationsdefaultLayoutdefaultShowThumbnailssignaturessignaturePlacementsannotationsonSignRequestonSignaturePlacementsChangeonAnnotationsChangeonSavefinalizeSignedDocumentsAsPdfletterheadTemplatethemelocale
The full exported types are available from the package:
import type {
AnnotationPlacement,
DocumentViewerProps,
DocumentViewerSaveMeta,
Signature,
SignatureInkColor,
SignaturePlacement,
} from "@zerohive/hive-viewer";Locale
You can override UI text through the locale prop.
Example:
<DocumentViewer
locale={{
"toolbar.sign": "Sign Document",
"toolbar.annotate": "Add Note",
"toolbar.save": "Save",
"toolbar.exportPdf": "Export as PDF",
}}
/>Theme
The built-in theme prop supports:
lightdark
<DocumentViewer theme="dark" />Browser And Hosting Notes
fileUrlsources must be reachable by the browser.- If the source is stored in a bucket, CORS must allow your frontend to fetch it.
- For private documents, many apps use signed URLs from their backend.
Current Rendering Notes
- PDF uses a page-based renderer.
- DOCX uses a browser-side page renderer in view mode and is best-effort, not a full Microsoft Word engine.
- Slides and spreadsheets are rendered with package-managed viewers and export pipelines.
- Rich-text save/export is visual rather than semantic word-processing output.
- Legacy office formats such as
doc,xls, andpptare accepted, butdocx,xlsx, andpptxare recommended for better fidelity.
Exports
The package exports:
import { DocumentViewer } from "@zerohive/hive-viewer";And these core types:
import type {
AnnotationPlacement,
AnnotationPlacementDraft,
AnnotationPatch,
DocumentMode,
DocumentViewerProps,
DocumentViewerSaveMeta,
PageLayout,
PlacementGeometryPatch,
Signature,
SignaturePlacement,
SignaturePlacementDraft,
SignatureSurfaceKind,
SupportedFileType,
} from "@zerohive/hive-viewer";Summary
The package gives consumers everything they need to wire document review into their own backend:
- document rendering
- signature placement
- annotations
- save/export as
base64 - structured placement metadata for persistence
The usual storage model is:
- upload returned
base64 - store the resulting file URL
- store
signaturePlacements - store
annotations
Then pass that data back into DocumentViewer whenever the document needs to be opened again.
