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-Callback after successful save
onError(error) => void-Callback on save error
onRestore(content) => void-Callback when content is restored

Storage Backends

localStorage (Default)

Persists content in the browser's localStorage:

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

sessionStorage

Persists content only for the current session:

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

Custom Backend

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 is saved and up to date
"saving"Save operation in progress
"unsaved"Unsaved changes pending
"error"Save 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()Manually trigger save
restore()Manually restore content

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

Handle save errors:

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

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

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.