Restore Utilities
Utilities for loading, restoring, and validating Excalidraw elements and application state from saved data.
Element Restoration
restoreElements
Restores elements from saved data, fixing inconsistencies and validating bindings.
function restoreElements<T extends ExcalidrawElement>(
targetElements: readonly T[] | undefined | null,
existingElements?: Readonly<ElementsMapOrArray> | null,
opts?: {
refreshDimensions?: boolean;
repairBindings?: boolean;
deleteInvisibleElements?: boolean;
}
): OrderedExcalidrawElement[]
targetElements
readonly ExcalidrawElement[]
required
Elements to restore
Existing local elements for context (e.g., for repairing arrow bindings)
Refresh text element dimensions
Repair arrow bindings and container relationships
opts.deleteInvisibleElements
Mark invisibly small elements as deleted
elements
OrderedExcalidrawElement[]
Restored and validated elements with fractional indices
Example:
import { restoreElements } from "@excalidraw/excalidraw";
const restoredElements = restoreElements(
loadedElements,
existingElements,
{
refreshDimensions: true,
repairBindings: true,
deleteInvisibleElements: true,
}
);
restoreElement
Restores a single element with proper type handling and migrations.
function restoreElement(
element: ExcalidrawElement,
targetElementsMap: Readonly<ElementsMap>,
existingElementsMap?: Readonly<ElementsMap> | null,
opts?: {
deleteInvisibleElements?: boolean;
}
): ExcalidrawElement | null
element
ExcalidrawElement
required
Element to restore
Map of all elements being restored
Map of existing elements for context
opts.deleteInvisibleElements
Whether to mark invisible elements as deleted
Restored element or null if invalid
What it does:
- Migrates legacy properties (e.g.,
font, boundElementIds, strokeSharpness)
- Normalizes dimensions and coordinates
- Repairs line/arrow points
- Validates and restores bindings
- Handles text wrapping and dimensions
- Restores image elements with proper status
App State Restoration
restoreAppState
Restores application state with default values and migrations.
function restoreAppState(
appState: ImportedDataState["appState"],
localAppState?: Partial<AppState> | null
): RestoredAppState
appState
Partial<AppState>
required
App state from saved data
Local app state to merge with
Complete app state with all properties initialized
Example:
import { restoreAppState } from "@excalidraw/excalidraw";
const appState = restoreAppState(
savedAppState,
currentAppState
);
What it restores:
- View state (zoom, scroll position)
- UI state (sidebar, theme, grid)
- Tool selection
- Export settings
- Collaboration state
- Legacy property migrations
Library Restoration
restoreLibraryItems
Restores library items from saved data.
function restoreLibraryItems(
libraryItems: ImportedDataState["libraryItems"],
defaultStatus: LibraryItem["status"]
): LibraryItem[]
defaultStatus
'published' | 'unpublished'
required
Default status for items without one
Restored library items with valid elements
Example:
import { restoreLibraryItems } from "@excalidraw/excalidraw";
const library = restoreLibraryItems(savedLibrary, "unpublished");
Version Management
bumpElementVersions
Bumps element versions relative to local elements (for conflict resolution).
function bumpElementVersions(
targetElements: readonly ExcalidrawElement[],
localElements?: Readonly<ElementsMapOrArray> | null
): ExcalidrawElement[]
targetElements
readonly ExcalidrawElement[]
required
Elements to bump versions for
Local elements to compare against
Elements with versions bumped where needed
Example:
import { bumpElementVersions } from "@excalidraw/excalidraw";
const importedElements = bumpElementVersions(
loadedElements,
scene.getElementsIncludingDeleted()
);
When to use:
- Importing files (to avoid conflicts with local elements)
- Handling collaborative updates
- Merging element collections
Data Serialization
serializeAsJSON
Serializes elements, app state, and files to JSON.
function serializeAsJSON(
elements: readonly ExcalidrawElement[],
appState: Partial<AppState>,
files: BinaryFiles,
type: "local" | "database"
): string
elements
readonly ExcalidrawElement[]
required
Elements to serialize
appState
Partial<AppState>
required
App state to serialize
type
'local' | 'database'
required
Serialization type (affects what’s included)
JSON string with formatted Excalidraw data
Example:
import { serializeAsJSON } from "@excalidraw/excalidraw";
const json = serializeAsJSON(
excalidrawAPI.getSceneElements(),
excalidrawAPI.getAppState(),
excalidrawAPI.getFiles(),
"local"
);
// Save to file or database
localStorage.setItem("excalidraw-data", json);
Type differences:
"local": Includes files, export-ready app state
"database": Strips files, minimal app state for collaboration
serializeLibraryAsJSON
Serializes library items to JSON.
function serializeLibraryAsJSON(libraryItems: LibraryItems): string
Library items to serialize
JSON string with library data
Example:
import { serializeLibraryAsJSON } from "@excalidraw/excalidraw";
const libraryJSON = serializeLibraryAsJSON(library);
localStorage.setItem("excalidraw-library", libraryJSON);
Data Validation
isValidExcalidrawData
Validates if data is a valid Excalidraw document.
function isValidExcalidrawData(data?: unknown): data is ImportedDataState
Whether data is valid Excalidraw format
Example:
import { isValidExcalidrawData } from "@excalidraw/excalidraw";
try {
const data = JSON.parse(fileContent);
if (isValidExcalidrawData(data)) {
const elements = restoreElements(data.elements);
const appState = restoreAppState(data.appState);
// Load into Excalidraw
} else {
console.error("Invalid Excalidraw file");
}
} catch (error) {
console.error("Failed to parse file", error);
}
Complete Restoration Example
import {
restoreElements,
restoreAppState,
restoreLibraryItems,
bumpElementVersions,
serializeAsJSON,
isValidExcalidrawData,
} from "@excalidraw/excalidraw";
class DataManager {
constructor(private excalidrawAPI) {}
// Load from file
async loadFromFile(file: File) {
const text = await file.text();
const data = JSON.parse(text);
if (!isValidExcalidrawData(data)) {
throw new Error("Invalid Excalidraw file");
}
// Get current state for merging
const currentElements = this.excalidrawAPI.getSceneElements();
const currentAppState = this.excalidrawAPI.getAppState();
// Restore elements with full validation
const restoredElements = restoreElements(
data.elements,
currentElements,
{
refreshDimensions: true,
repairBindings: true,
deleteInvisibleElements: true,
}
);
// Bump versions to avoid conflicts
const importedElements = bumpElementVersions(
restoredElements,
currentElements
);
// Restore app state
const appState = restoreAppState(data.appState, currentAppState);
// Update Excalidraw
this.excalidrawAPI.updateScene({
elements: importedElements,
appState,
});
console.log(`Loaded ${importedElements.length} elements`);
}
// Save to file
saveToFile(filename: string) {
const json = serializeAsJSON(
this.excalidrawAPI.getSceneElements(),
this.excalidrawAPI.getAppState(),
this.excalidrawAPI.getFiles(),
"local"
);
const blob = new Blob([json], {
type: "application/vnd.excalidraw+json",
});
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
}
// Auto-save to localStorage
autoSave() {
const json = serializeAsJSON(
this.excalidrawAPI.getSceneElements(),
this.excalidrawAPI.getAppState(),
this.excalidrawAPI.getFiles(),
"database" // Use database format for localStorage
);
localStorage.setItem("excalidraw-autosave", json);
console.log("Auto-saved to localStorage");
}
// Restore from auto-save
restoreAutoSave() {
const json = localStorage.getItem("excalidraw-autosave");
if (!json) {
return false;
}
try {
const data = JSON.parse(json);
if (isValidExcalidrawData(data)) {
const elements = restoreElements(data.elements, null, {
repairBindings: true,
});
const appState = restoreAppState(data.appState);
this.excalidrawAPI.updateScene({
elements,
appState,
});
return true;
}
} catch (error) {
console.error("Failed to restore auto-save", error);
}
return false;
}
// Export library
exportLibrary() {
const library = this.excalidrawAPI.getLibrary();
const json = serializeLibraryAsJSON(library);
const blob = new Blob([json], {
type: "application/vnd.excalidrawlib+json",
});
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "library.excalidrawlib";
link.click();
URL.revokeObjectURL(url);
}
// Import library
async importLibrary(file: File) {
const text = await file.text();
const data = JSON.parse(text);
const items = restoreLibraryItems(
data.libraryItems || data.library,
"unpublished"
);
// Merge with existing library
const currentLibrary = this.excalidrawAPI.getLibrary();
this.excalidrawAPI.updateLibrary([
...currentLibrary,
...items,
]);
console.log(`Imported ${items.length} library items`);
}
}
// Usage
const dataManager = new DataManager(excalidrawAPI);
// Set up auto-save
setInterval(() => {
dataManager.autoSave();
}, 30000); // Every 30 seconds
// Restore on load
if (dataManager.restoreAutoSave()) {
console.log("Restored from auto-save");
}
See Also