# Unsaved Changes

**MUST** · **ID:** `forms-unsaved-changes` · **Category:** forms
**Source:** [Vercel](https://github.com/vercel-labs/agent-skills/blob/main/skills/web-design-guidelines/SKILL.md)
**Interactive version:** https://ui-guides-agent-rules.netlify.app/principles/forms-unsaved-changes

> MUST: Warn on unsaved changes before navigation

Warn before navigation when data could be lost

Track form modifications and show a confirmation dialog if the user tries to navigate away or close the tab with unsaved changes. Use the beforeunload event for page unload and custom logic for in-app navigation.

## Bad — do not do this

`forms-unsaved-changes-bad`

```tsx
import { useState } from 'react';

export function UnsavedChangesBad() {
  const [content, setContent] = useState('');

  return (
    <div className="w-full max-w-sm">
      <form className="space-y-4">
        <div>
          <label htmlFor="bad-unsaved-content" className="block text-sm font-medium text-foreground mb-1">
            Your Post
          </label>
          <textarea
            id="bad-unsaved-content"
            value={content}
            onChange={(e) => setContent(e.target.value)}
            rows={4}
            className="w-full px-3 py-2 border border-border rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
            placeholder="Write something..."
          />
        </div>
        <div className="flex gap-2">
          <button
            type="submit"
            className="px-4 py-2 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors"
          >
            Save
          </button>
          <a
            href="#"
            className="px-4 py-2 bg-muted text-foreground rounded-lg hover:bg-accent transition-colors"
          >
            Cancel
          </a>
        </div>
      </form>
      <p className="text-xs text-error mt-4">
        No warning when navigating away with unsaved changes
      </p>
    </div>
  );
}
```

## Good — do this

`forms-unsaved-changes-good`

```tsx
import { useState, useEffect } from 'react';

export function UnsavedChangesGood() {
  const [content, setContent] = useState('');
  const [saved, setSaved] = useState(true);

  useEffect(() => {
    if (content) {
      setSaved(false);
    }

    const handleBeforeUnload = (e: BeforeUnloadEvent) => {
      if (!saved && content) {
        e.preventDefault();
        e.returnValue = '';
      }
    };

    window.addEventListener('beforeunload', handleBeforeUnload);
    return () => window.removeEventListener('beforeunload', handleBeforeUnload);
  }, [content, saved]);

  const handleSave = (e: React.FormEvent) => {
    e.preventDefault();
    setSaved(true);
    alert('Saved!');
  };

  const handleCancel = (e: React.MouseEvent) => {
    e.preventDefault();
    if (!saved && content) {
      if (confirm('You have unsaved changes. Are you sure you want to cancel?')) {
        setContent('');
        setSaved(true);
      }
    } else {
      setContent('');
    }
  };

  return (
    <div className="w-full max-w-sm">
      <form onSubmit={handleSave} className="space-y-4">
        <div>
          <label htmlFor="good-unsaved-content" className="block text-sm font-medium text-foreground mb-1">
            Your Post {!saved && <span className="text-warning">(unsaved)</span>}
          </label>
          <textarea
            id="good-unsaved-content"
            value={content}
            onChange={(e) => setContent(e.target.value)}
            rows={4}
            className="w-full px-3 py-2 border border-border rounded-lg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
            placeholder="Write something..."
          />
        </div>
        <div className="flex gap-2">
          <button
            type="submit"
            className="px-4 py-2 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors"
          >
            Save
          </button>
          <a
            href="#"
            onClick={handleCancel}
            className="px-4 py-2 bg-muted text-foreground rounded-lg hover:bg-accent transition-colors"
          >
            Cancel
          </a>
        </div>
      </form>
      <p className="text-xs text-success mt-4">
        Warns before navigation with unsaved changes
      </p>
    </div>
  );
}
```

## References

- [beforeunload event](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event)
