---
title: "Save & store electronic signatures in JavaScript PDF viewer | Nutrient"
canonical_url: "https://www.nutrient.io/guides/web/signatures/signature-storage/"
md_url: "https://www.nutrient.io/guides/web/signatures/signature-storage.md"
last_updated: "2026-05-15T19:10:05.100Z"
description: "Discover effective methods for storing digital signatures safely and securely. Optimize your workflow with our comprehensive signature storage guide."
---

# Save and store electronic signatures in our JavaScript viewer

Nutrient allows you to implement your own mechanism for storing signatures.

[Try for Free](https://www.nutrient.io/try)

[Launch Demo](https://www.nutrient.io/demo/signature-storage/)

If you provide such a mechanism, then signatures may optionally be saved at the same time they’re added to a document. The saving of signatures is based on the  [`SIGNATURE_SAVE_MODE`](https://www.nutrient.io/api/web/NutrientViewer.Options.html#SIGNATURE_SAVE_MODE) : This defaults to showing a toggle in the UI that allows the user to choose whether to save, but you can change this option to hide the toggle and instead always save signatures without giving the user the choice.![Screenshot of enabled toggle for saving signatures.](@/assets/guides/web/signatures/signature-storage/save-toggle.png)

If you provide stored signatures to Nutrient, then when the user selects a signature form field or the signature tool, the list of stored signatures will be shown instead of the signature creation UI.![Screenshot showing list with two signatures: John Appleseed and J.A.](@/assets/guides/web/signatures/signature-storage/saved-signatures.png)

To do this, you can register event listeners for the [`storedSignatures.create`](https://www.nutrient.io/api/web/NutrientViewer.Instance.html#~StoredSignatureCreateEvent) and [`storedSignatures.delete`](https://www.nutrient.io/api/web/NutrientViewer.Instance.html#~StoredSignatureDeleteEvent) events. Once these event listeners are registered, the signatures UI is updated to display a checkbox to allow users to decide if they want their signatures stored or not.

`storedSignatures.create` is invoked with the signature annotation created by the user. There, you can serialize the signature and send it to a backend server, or add it to a browser storage mechanism such as IndexedDB or local storage.

The `storedSignatures.create` payload returns `null` values for the annotation `id` and `name`. We return these values because the created signature isn’t attached to the document, hence it isn’t assigned an `id` or `name`. If you want to retrieve a complete list of values of the signature annotation, we suggest listening to the `annotations.create` event.

As an example, the code below shows how to add signatures using `window.localStorage`. Note that local storage is a blocking API, so it isn’t ideal for production applications, especially when dealing with images:

```js

const STORAGE_KEY = 'signatures_storage';
const ATTACHMENTS_KEY = 'attachments_storage';

instance.addEventListener('storedSignatures.create', async (annotation) => {
	const signaturesString = localStorage.getItem(STORAGE_KEY);
	const storedSignatures = signaturesString? JSON.parse(signaturesString)
		: [];

	const serializedAnnotation = NutrientViewer.Annotations.toSerializableObject(
		annotation,
	);
	if (annotation.imageAttachmentId) {
		const attachment = await instance.getAttachment(
			annotation.imageAttachmentId,
		);

		// Create the data URL and add it to local storage.
		// Note: This is done only for demonstration purposes.
		// Storing potentially large chunks of data using local storage is
		// considered bad practice due to the synchronous nature of the API.
		// For production applications, consider alternatives such as
		// dedicated backend storage or IndexedDB.
		const url = await fileToDataURL(attachment);
		const attachmentsString = localStorage.getItem(ATTACHMENTS_KEY);
		const attachmentsArray = attachmentsString? JSON.parse(attachmentsString)
			: [];
		attachmentsArray.push({ url, id: annotation.imageAttachmentId });
		// Separate the `localStorage` item to store attachments.
		localStorage.setItem(ATTACHMENTS_KEY, JSON.stringify(attachmentsArray));
	}
	storedSignatures.push(serializedAnnotation);
	localStorage.setItem(STORAGE_KEY, JSON.stringify(storedSignatures));
	// Add a new annotation so that it renders as part of the UI on the current session.
	instance.setStoredSignatures((signatures) => signatures.push(annotation));
});

```

Similarly, it’s necessary to implement deleting the signature from the underlying storage when the user chooses to delete one of the existing records through the UI:

```js

instance.addEventListener('storedSignatures.delete', (annotation) => {
	const signaturesString = localStorage.getItem(STORAGE_KEY);
	const storedSignatures = signaturesString? JSON.parse(signaturesString)
		: [];
	const annotations = storedSignatures.map(
		NutrientViewer.Annotations.fromSerializableObject,
	);
	const updatedAnnotations = annotations.filter(
		(currentAnnotation) =>!currentAnnotation.equals(annotation),
	);
	localStorage.setItem(
		STORAGE_KEY,
		JSON.stringify(
			updatedAnnotations.map(NutrientViewer.Annotations.toSerializableObject),
		),
	);
	// Use the `setStoredSignatures` API so that the current UI is properly updated.
	instance.setStoredSignatures((signatures) =>
		signatures.filter((signature) =>!signature.equals(annotation)),
	);

	if (annotation.imageAttachmentId) {
		// Remove the attachment from the array.
		const attachmentsString = localStorage.getItem(ATTACHMENTS_KEY);
		if (attachmentsString) {
			let attachmentsArray = JSON.parse(attachmentsString);
			attachmentsArray = attachmentsArray.filter(
				(attachment) => attachment.id!== annotation.imageAttachmentId,
			);
			localStorage.setItem(
				ATTACHMENTS_KEY,
				JSON.stringify(attachmentsArray),
			);
		}
	}
});

```

To set the list of available signatures to pick from, you can use the [`NutrientViewer.Configuration#populateStoredSignatures`](https://www.nutrient.io/api/web/NutrientViewer.Configuration.html#populateStoredSignatures) configuration option or the [`NutrientViewer.Instance#setStoredSignatures`](https://www.nutrient.io/api/web/NutrientViewer.Instance.html#setStoredSignatures) instance method:

```js

const signaturesString = localStorage.getItem(STORAGE_KEY);
const storedSignatures = JSON.parse(signaturesString);
// Construct annotations from serialized entries and call the `setStoredSignatures` API.
const list = NutrientViewer.Immutable.List(
	storedSignatures.map(NutrientViewer.Annotations.fromSerializableObject),
);
instance.setStoredSignatures(list);

// Retrieve attachments and add them to the instance.
const attachmentsString = localStorage.getItem(ATTACHMENTS_KEY);
if (attachmentsString) {
	const attachmentsArray = JSON.parse(attachmentsString);
	// Instantiate blob objects from the data URLs on local storage.
	const blobs = await Promise.all(
		attachmentsArray.map(({ url }) => fetch(url).then((res) => res.blob())),
	);
	// Create an attachment for each blob.
	blobs.forEach(instance.createAttachment);
}

```
---

## Related pages

- [Add electronic signatures to PDFs with JavaScript](/guides/web/signatures/adding-an-electronic-signature.md)
- [Sign PDFs with certificates using JavaScript](/guides/web/signatures/using-electronic-signatures-and-digital-signatures-together.md)

