Save Annotations to External Storage

You have two options to save annotation changes to external storage: saving the changes only, or saving the whole PDF.

Saving the Changes Only

Our recommendation for storing changes to annotations is through Instant JSON. Importing and exporting a JSON file with the changes made to a document instead of doing this with the full PDF each time significantly reduces the bandwidth used.

If you just want to save the annotation changes made by the user on the original PDF, you can use Instance#exportInstantJSON to export a JSON containing all the changes. You can store this separately and apply it when you load the original PDF next time using Configuration#instantJSON (Standalone mode only). With this approach, you won’t have to repeatedly save the PDF file, and you can use the original PDF every time.

Please see the importing and exporting section of our Instant JSON reference guide for examples of how to use Instant JSON.

When using the Server-Backed mode, you can still use Instance#exportInstantJSON() to export annotation changes to external storage. Follow the Server-Backed guides for an in-depth example of how to use Instant JSON in Server-Backed mode.

Saving the Whole PDF

You can export the modified PDF as an array buffer by using Instance#exportPDF and triggering a download, as explained in the Save a Document to Local Storage guide.

Ensuring Annotations Have Been Persisted

If you want to ensure that an annotation has been persisted by the backend, you can use the Instance.ensureChangesSaved endpoint. This endpoint takes an array of changes and returns a promise that resolves to the array of changes in their persisted state as soon as they’re saved by the server. This means if you update the annotation before it has been saved by the server, Instance.ensureChangesSaved will return the updated annotation, even though Instance.ensureChangesSaved might have been called before the annotation was updated. Since this method takes exactly the same argument that’s returned by the Instance#create method, you can easily chain the calls. The ID can be used with the backend API and is unique inside a document:

PSPDFKit.load(configuration).then(async (instance) => {
  const createdAnnotations = await instance.create(newAnnotation);
  const [savedAnnotation] = await instance.ensureChangesSaved(
  console.log(; // => '01BS964AM5Z01J9MKBK64F22BQ'
  autoSaveMode: PSPDFKit.AutoSaveMode.DISABLED
}).then(async (instance) => {
  const newAnnotation = new PSPDFKit.Annotations.TextAnnotation({
    pageIndex: 0,
    boundingBox: new PSPDFKit.Geometry.Rect({
      left: 100,
      top: 200,
      width: 150,
      height: 75
    text: {
      format: "plain",
      value: "foo"
  console.log(newAnnotation.text); // => 'foo'
  const createdAnnotations = await instance.create(newAnnotation);
  const savedAnnotationPromise = instance.ensureChangesSaved(
  const updatedAnnotations = await instance.update(
    createdAnnotations[0].set("text", "bar")
  const [savedAnnotation] = await savedAnnotationPromise;
  console.log(savedAnnotation.text); // => 'bar'