← ./articles

In Tauri Apps, Ctrl+S Needs preventDefault Before Your Save Handler

Tauri apps run your frontend inside a WebView. That means browser keyboard behavior can still appear inside a desktop app.

The common surprise is Ctrl+S. You add a save handler, press the shortcut, and the native "Save Page As" dialog appears. The app did not fully intercept the shortcut.

The missing line is usually event.preventDefault().

The symptom: Save Page appears inside a desktop app

The failure looks strange because the app is not a normal website:

  • Ctrl+S should save the current project
  • the handler may even run
  • the WebView still opens a native save-page dialog
  • users see browser behavior inside a desktop workflow

That is a product bug, not only a technical nuisance. A desktop editor must make core shortcuts feel native.

Intercept the shortcut and stop the browser default

Use one document-level handler for app shortcuts:

document.addEventListener("keydown", (event) => {
  const mod = event.ctrlKey || event.metaKey;

  if (mod && event.key.toLowerCase() === "s") {
    event.preventDefault();
    saveCurrentDocument();
  }
});

metaKey covers macOS Command shortcuts. ctrlKey covers Windows and Linux.

The important part is that preventDefault() runs in every shortcut branch that replaces browser behavior.

Do not call the handler and forget the default

This is not enough:

if ((event.ctrlKey || event.metaKey) && event.key === "s") {
  saveCurrentDocument();
}

The app handler can run and the browser default can still run. That produces double behavior: your app saves while the WebView opens a save-page dialog.

Use the same pattern for file actions:

document.addEventListener("keydown", (event) => {
  const mod = event.ctrlKey || event.metaKey;
  const key = event.key.toLowerCase();

  if (mod && key === "s" && !event.shiftKey) {
    event.preventDefault();
    saveCurrentDocument();
  }

  if (mod && key === "o") {
    event.preventDefault();
    openProject();
  }

  if (mod && event.shiftKey && key === "s") {
    event.preventDefault();
    saveAs();
  }
});

Keep the shortcut map explicit. Hidden shortcut behavior is hard to test and harder for users to learn.

Show shortcuts in the UI

If an action has a keyboard shortcut, show it near the command:

<button onClick={saveCurrentDocument}>
  Save <kbd>Ctrl</kbd><kbd>S</kbd>
</button>

Use Cmd instead of Ctrl on macOS if you display platform-specific labels.

When to use a plugin

For a few app-level shortcuts, a frontend keydown handler is often enough.

If you need broad suppression of browser shortcuts across a complex app, a Tauri plugin such as tauri-plugin-prevent-default can centralize that behavior. Even then, keep app-specific commands explicit so the UI and tests know what each shortcut does.

Verification checklist

Check these cases:

  • Ctrl+S saves without opening Save Page
  • Cmd+S works on macOS if supported
  • focused inputs do not break intended text editing shortcuts
  • shortcuts are removed on component unmount if registered inside React
  • UI labels match the actual shortcuts
  • automated tests or manual smoke tests cover the native dialog regression

References