Skip to main content
When a user changes a single setting — say, the app theme — you should not need to read the entire config, modify it in memory, and write all fields back. config.patch() handles this for you: it deep-merges the partial object you provide into the stored config, leaving every key you omit completely untouched. This guide explains how patching works, how it interacts with keyring-protected fields, and how to handle configs that might not yet exist.

How Patching Works

config.patch(partial) accepts a Partial<InferUnlocked<S>> and sends it to the plugin, which reads the current file, merges your partial data on top, and writes the result back. Only the keys present in your partial object are updated; everything else is preserved exactly as stored. The merge follows JSON Merge Patch semantics (RFC 7396):
  • Omitting a key leaves the stored value unchanged.
  • Setting a key to null writes null into the stored file — it does not delete or skip the key.
  • Providing a nested object recursively merges at that level, so you can update a single nested property without supplying the rest of the object.
config.patch() returns a LazyPatchEntry. You complete the operation by chaining a terminal method.
Terminal methodDescriptionReturns
.run()Patch without keyringPromise<PatchedConfig<S>>
.lock(opts).run()Patch and store keyring secretsPromise<PatchedConfig<S>>
.unlock(opts)Patch, store keyring secrets, return unlocked resultPromise<UnlockedConfig<S>>
.createIfMissing()(modifier) Create config if not found instead of throwingLazyPatchEntry<S>
Setting a key to null in your patch object writes null to disk — it does not clear the field the way you might expect from other update APIs. If you want to reset a field to a default value, provide that default value explicitly, or use config.save() to replace the entire file.

Basic Patch

Pass only the fields you want to change. All other stored keys remain as they are.
import {
  BaseDirectory,
  Configurate,
  JsonProvider,
  defineConfig,
  keyring,
} from "tauri-plugin-configurate-api";

const schema = defineConfig({
  theme: String,
  database: {
    host: String,
    password: keyring(String, { id: "db-password" }),
  },
});

const config = new Configurate({
  schema,
  fileName: "app.json",
  baseDir: BaseDirectory.AppConfig,
  provider: JsonProvider(),
});

const KEYRING = { service: "my-app", account: "default" };

// Only `theme` changes; `database` is untouched
const patched = await config.patch({ theme: "dark" }).run();
console.log(patched.data.theme); // "dark"
The returned PatchedConfig<S> exposes data typed as Partial<InferLocked<S>> — reflecting only the keys that were part of the patch, with keyring fields as null in the locked form.

Patching Keyring Fields

If your partial data includes a keyring-protected field, you must chain .lock(opts) before .run() so the plugin knows where to store the updated secret.
// Update the database host and rotate the password
const patched = await config
  .patch({ database: { host: "db.prod.example.com", password: "new-secret" } })
  .lock(KEYRING)
  .run();

Get Unlocked Data After Patching

Chain .unlock(opts) instead of .lock(opts).run() to receive an UnlockedConfig<S> with keyring fields already populated — useful when you need to display or act on the new values immediately after patching.
const unlocked = await config
  .patch({ theme: "light" })
  .unlock(KEYRING);

console.log(unlocked.data.theme);                   // "light"
console.log(unlocked.data.database.password);       // "secret" (from keyring)

Create If Missing

By default, patching a config that does not yet exist throws an error. Chain .createIfMissing() before .run() to create the file with the provided partial data instead of throwing.
Patching a config that does not exist will throw unless you chain .createIfMissing(). If you are unsure whether the file has been created yet, either check with config.exists() first or use .createIfMissing() as a safe default.
// Creates "app.json" with { theme: "dark" } if it doesn't exist yet;
// otherwise merges { theme: "dark" } into the existing file.
await config.patch({ theme: "dark" }).createIfMissing().run();
You can combine .createIfMissing() with .lock() when your partial data contains keyring fields:
await config
  .patch({ theme: "dark", database: { host: "localhost", password: "secret" } })
  .createIfMissing()
  .lock(KEYRING)
  .run();