Skip to content

Auto-Save

Auto-save persists editor content automatically.

Basic Usage

tsx
import { useVizelEditor, useVizelAutoSave, VizelEditor, VizelSaveIndicator } from '@vizel/react';

function Editor() {
  const editor = useVizelEditor();
  
  const { status, lastSaved } = useVizelAutoSave(() => editor, {
    debounceMs: 2000,
    storage: 'localStorage',
    key: 'my-editor-content',
  });

  return (
    <div>
      <VizelEditor editor={editor} />
      <VizelSaveIndicator status={status} lastSaved={lastSaved} />
    </div>
  );
}
vue
<script setup lang="ts">
import { useVizelEditor, useVizelAutoSave } from '@vizel/vue';
import { VizelEditor, VizelSaveIndicator } from '@vizel/vue';

const editor = useVizelEditor();

const { status, lastSaved } = useVizelAutoSave(() => editor.value, {
  debounceMs: 2000,
  storage: 'localStorage',
  key: 'my-editor-content',
});
</script>

<template>
  <div>
    <VizelEditor :editor="editor" />
    <VizelSaveIndicator :status="status" :lastSaved="lastSaved" />
  </div>
</template>
svelte
<script lang="ts">
  import { createVizelEditor, createVizelAutoSave, VizelEditor, VizelSaveIndicator } from '@vizel/svelte';

  const editor = createVizelEditor();
  
  const autoSave = createVizelAutoSave(() => editor.current, {
    debounceMs: 2000,
    storage: 'localStorage',
    key: 'my-editor-content',
  });
</script>

<VizelEditor editor={editor.current} />
<VizelSaveIndicator status={autoSave.status} lastSaved={autoSave.lastSaved} />

Options

AutoSaveOptions

PropertyTypeDefaultDescription
enabledbooleantrueEnable auto-save
debounceMsnumber1000Debounce delay in milliseconds
storageStorageBackend"localStorage"Storage backend
keystring"vizel-content"Storage key
onSave(content) => void-Fires after a successful save
onError(error) => void-Fires on a save error
onRestore(content) => void-Fires when the system restores content

Storage Backends

localStorage (Default)

This backend persists content in the browser's localStorage:

typescript
const { status } = useVizelAutoSave(() => editor, {
  storage: 'localStorage',
  key: 'my-editor-content',
});

sessionStorage

This backend persists content only for the current session:

typescript
const { status } = useVizelAutoSave(() => editor, {
  storage: 'sessionStorage',
  key: 'my-editor-content',
});

Custom Backend

You can implement your own storage backend for server-side persistence:

typescript
const { status } = useVizelAutoSave(() => editor, {
  storage: {
    save: async (content) => {
      await fetch('/api/save', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(content),
      });
    },
    load: async () => {
      const response = await fetch('/api/content');
      if (!response.ok) return null;
      return response.json();
    },
  },
});

Return Values

Status

ValueDescription
"saved"Content has been saved and is up to date
"saving"A save operation is in progress
"unsaved"Unsaved changes exist
"error"The last save attempt failed

Properties

PropertyTypeDescription
statusSaveStatusCurrent save status
hasUnsavedChangesbooleanWhether there are unsaved changes
lastSavedDate | nullTimestamp of last successful save
errorError | nullLast error if status is "error"

Methods

MethodDescription
save()Triggers a save manually
restore()Restores content manually

Manual Save/Restore

typescript
const { save, restore } = useVizelAutoSave(() => editor, { storage: 'localStorage' });

// Trigger manual save
await save();

// Restore from storage
const content = await restore();
if (content) {
  editor.commands.setContent(content);
}

Save Indicator

The VizelSaveIndicator component displays the current save status:

tsx
import { VizelSaveIndicator } from '@vizel/react';

<VizelSaveIndicator 
  status={status} 
  lastSaved={lastSaved}
  className="my-indicator"
/>
vue
import { VizelSaveIndicator } from '@vizel/vue';

<VizelSaveIndicator 
  :status="status" 
  :lastSaved="lastSaved"
  class="my-indicator"
/>
svelte
import { VizelSaveIndicator } from '@vizel/svelte';

<VizelSaveIndicator 
  status={autoSave.status} 
  lastSaved={autoSave.lastSaved}
  class="my-indicator"
/>

Custom Indicator

tsx
function CustomIndicator({ status, lastSaved }) {
  const formatTime = (date: Date) => {
    return new Intl.RelativeTimeFormat('en', { numeric: 'auto' })
      .format(Math.round((date.getTime() - Date.now()) / 60000), 'minute');
  };

  return (
    <div className="save-indicator">
      {status === 'saving' && <span>Saving...</span>}
      {status === 'saved' && lastSaved && (
        <span>Saved {formatTime(lastSaved)}</span>
      )}
      {status === 'unsaved' && <span>Unsaved changes</span>}
      {status === 'error' && <span className="error">Save failed</span>}
    </div>
  );
}

Debouncing

The debounceMs option controls how long to wait after the last change before saving:

typescript
// Save 500ms after last change (more responsive)
useVizelAutoSave(() => editor, { debounceMs: 500 });

// Save 5 seconds after last change (less frequent)
useVizelAutoSave(() => editor, { debounceMs: 5000 });

Error Handling

You can handle save errors with the onError callback:

typescript
const { status, error } = useVizelAutoSave(() => editor, {
  storage: {
    save: async (content) => {
      const response = await fetch('/api/save', {
        method: 'POST',
        body: JSON.stringify(content),
      });
      if (!response.ok) {
        throw new Error('Failed to save');
      }
    },
  },
  onError: (error) => {
    console.error('Auto-save failed:', error);
    // Show notification to user
    toast.error('Failed to save. Please try again.');
  },
});

Restoring Content on Load

You can restore saved content when the editor initializes:

tsx
import { useEffect } from 'react';
import { useVizelEditor, useVizelAutoSave, VizelEditor } from '@vizel/react';

function Editor() {
  const editor = useVizelEditor();
  
  const { restore } = useVizelAutoSave(() => editor, {
    storage: 'localStorage',
    key: 'my-editor-content',
    onRestore: (content) => {
      if (content) {
        console.log('Restored saved content');
      }
    },
  });

  useEffect(() => {
    if (editor) {
      restore().then((content) => {
        if (content) {
          editor.commands.setContent(content);
        }
      });
    }
  }, [editor]);

  return <VizelEditor editor={editor} />;
}
vue
<script setup lang="ts">
import { watch } from 'vue';
import { useVizelEditor, useVizelAutoSave } from '@vizel/vue';

const editor = useVizelEditor();

const { restore } = useVizelAutoSave(() => editor.value, {
  storage: 'localStorage',
  key: 'my-editor-content',
});

watch(editor, async (ed) => {
  if (ed) {
    const content = await restore();
    if (content) {
      ed.commands.setContent(content);
    }
  }
}, { once: true });
</script>
svelte
<script lang="ts">
  import { onMount } from 'svelte';
  import { createVizelEditor, createVizelAutoSave } from '@vizel/svelte';

  const editor = createVizelEditor();
  
  const autoSave = createVizelAutoSave(() => editor.current, {
    storage: 'localStorage',
    key: 'my-editor-content',
  });

  onMount(async () => {
    const content = await autoSave.restore();
    if (content && editor.current) {
      editor.current.commands.setContent(content);
    }
  });
</script>

Disabling Auto-Save

You can temporarily disable auto-save:

typescript
const { status } = useVizelAutoSave(() => editor, {
  enabled: false, // Disable auto-save
});

Or conditionally based on state:

typescript
const [autoSaveEnabled, setAutoSaveEnabled] = useState(true);

const { status } = useVizelAutoSave(() => editor, {
  enabled: autoSaveEnabled,
});

Next Steps

Released under the MIT License.