tauri-plugin-configurate.
fn main() {
tauri::Builder::default()
.plugin(tauri_plugin_configurate::init())
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
Finally, grant the required permissions in your capability file (e.g.
src-tauri/capabilities/default.json):configurate:allow-unlock is required whenever you call .unlock() on a load, create, or save operation. If your schema has no keyring fields you can omit it.Use
defineConfig() to declare the shape of your configuration. Each field maps to a TypeScript primitive constructor (String, Number, Boolean), a nested object, an array, or a special marker:keyring(typeCtor, opts) — stores the field in the OS keyring instead of on disk. Requires a unique id string.optional(schema) — marks a field as optional (may be absent from stored config without failing validation).import {
BaseDirectory,
Configurate,
JsonProvider,
defineConfig,
keyring,
optional,
} from "tauri-plugin-configurate-api";
const schema = defineConfig({
theme: String,
fontSize: Number,
notifications: optional(Boolean),
server: {
host: String,
apiKey: keyring(String, { id: "server-api-key" }),
},
});
StringstringNumbernumber (must be finite)Booleanbooleanoptional(schema)undefined when absentkeyring(Type, { id })null when locked{ ... }[Type]Every
keyring() call requires a unique id string within the schema. The id must not be empty or contain / — it is used as part of the OS keyring user string.Instantiate
Configurate with your schema, a file name, a base directory, and a storage provider. This object is your entry point to all CRUD operations.const config = new Configurate({
schema,
fileName: "app.json",
baseDir: BaseDirectory.AppConfig,
provider: JsonProvider(),
});
fileNamebaseDirBaseDirectory value. By default, only app-scoped directories (AppConfig, AppData, AppLocalData, etc.) are allowed over IPC.providerJsonProvider() writes human-readable, pretty-printed JSON. Other options: YmlProvider(), TomlProvider(), and BinaryProvider(opts?).You can create multiple
Configurate instances with different fileName values to manage separate config files — for example, one for app-wide settings and another for per-user profiles.Call
config.create(data) to write the initial configuration. When your schema contains keyring() fields, chain .lock(KEYRING) before calling .run() so the plugin knows which OS keyring service and account to store the secrets under.const KEYRING = { service: "my-app", account: "default" };
await config
.create({
theme: "dark",
fontSize: 14,
notifications: true,
server: { host: "api.example.com", apiKey: "sk-secret" },
})
.lock(KEYRING)
.run();
.lock(KEYRING) separates the keyring fields from the rest of the payload and stores them in the OS keychain under the given service and account..run() executes the operation and returns a LockedConfig object where keyring fields appear as null..lock() and call .run() directly.KeyringOptions (service and account) are yours to choose — typically your app name and a logical account label like "default". They must be non-empty and contain no control characters.Use
config.load() to read the stored file. Chain .unlock(KEYRING) to populate keyring fields from the OS keychain, or call .run() to load in locked form (keyring fields will be null).// Load with keyring secrets populated:
const { data } = await config.load().unlock(KEYRING);
console.log(data.theme); // "dark"
console.log(data.server.apiKey); // "sk-secret" (from OS keyring)
// Or load without unlocking (apiKey is null):
const locked = await config.load().run();
console.log(locked.data.server.apiKey); // null
// You can also unlock later from a locked result:
const unlocked = await locked.unlock(KEYRING);
console.log(unlocked.data.server.apiKey); // "sk-secret"
Call
unlocked.lock() when you are done with sensitive values to revoke access through that instance. After calling .lock(), any subsequent read of unlocked.data throws an error. This is an API-level access guard — JavaScript’s garbage collector manages the underlying memory.Use
config.save(data) to fully replace the stored config with new data. Unlike patch, save overwrites every key — it is a complete replacement.await config
.save({
theme: "light",
fontSize: 16,
notifications: false,
server: { host: "api.example.com", apiKey: "sk-secret" },
})
.lock(KEYRING)
.run();
Use
config.patch(partial) to update only the keys you provide. All other keys in the stored file are left untouched — this is a deep merge, not a full replacement.Use
config.exists() to check whether the config file is present without loading its contents, and config.list() to enumerate all config files in the same directory.const exists = await config.exists();
console.log(exists); // true or false
const files = await config.list();
console.log(files); // e.g. ["app.json", "profiles.json"]
Use
config.delete(keyringOpts?) to remove the config file. Pass your KeyringOptions to also delete the associated keyring entries so no secrets are left behind.Complete example
Here is the full end-to-end flow in a single TypeScript snippet you can drop straight into your Tauri frontend:Next steps
Schema Definition
Learn every schema value type: primitives, nested objects, arrays, optional fields, and keyring markers.
Storage Providers
Compare JSON, YAML, TOML, and encrypted Binary providers and choose the right one for your app.
OS Keyring
Understand how secrets are stored and retrieved from the native OS keychain on Linux, Windows, and macOS.
CRUD Operations
Deep-dive into create, load, save, patch, reset, and delete — including batch operations and file watching.