Skip to main content
This guide walks you through the minimum steps to get a working configuration file in a Tauri v2 app. By the end you will have a typed config that creates and loads values — including a keyring-protected secret.
For complete installation options (manual Cargo.toml setup, all package managers, and capability details), see the Installation page.
1

Install the plugin

The fastest way to add both the Rust crate and the npm package in one command is through the Tauri CLI:
npm run tauri add configurate
This adds tauri-plugin-configurate to src-tauri/Cargo.toml and installs tauri-plugin-configurate-api as an npm dependency.See Installation if you prefer to set up each package manually.
2

Register the Rust plugin

Open src-tauri/src/lib.rs and call tauri_plugin_configurate::init() in your Tauri builder:
src-tauri/src/lib.rs
pub fn run() {
    tauri::Builder::default()
        .plugin(tauri_plugin_configurate::init())
        .run(tauri::generate_context!())
        .expect("error while running tauri application");
}
3

Grant permissions

Add the default permission set to your capability file. This enables all standard operations (create, load, save, patch, delete, and more):
src-tauri/capabilities/default.json
{
  "permissions": ["configurate:default"]
}
configurate:default grants all built-in permissions. See the Permissions overview if you need to restrict access to specific operations.
4

Define your schema

In your frontend code, import defineConfig, keyring, and optional to describe your config shape. TypeScript infers all value types from the schema automatically.
src/lib/config.ts
import { defineConfig, keyring, optional } from "tauri-plugin-configurate-api";

export const appSchema = defineConfig({
  theme: String,
  language: String,
  fontSize: optional(Number),
  database: {
    host: String,
    // "password" is stored in the OS keyring — it never touches disk
    password: keyring(String, { id: "db-password" }),
  },
});
Each keyring() field requires a unique id string within the schema. defineConfig() validates this at runtime and throws if any IDs are duplicated.
5

Create a Configurate instance and run a create/load cycle

Instantiate Configurate with your schema, a file name, a base directory, and a provider. Then create the config and load it back.
src/lib/config.ts
import {
  BaseDirectory,
  Configurate,
  JsonProvider,
} from "tauri-plugin-configurate-api";
import { appSchema } from "./schema";

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

// Keyring credentials — identifies where secrets are stored in the OS keyring
const KEYRING = { service: "my-app", account: "default" };

// Create the config file and store the password in the OS keyring
await config
  .create({
    theme: "dark",
    language: "en",
    database: { host: "localhost", password: "secret" },
  })
  .lock(KEYRING)
  .run();

// Load without unlocking — keyring fields come back as null
const locked = await config.load().run();
console.log(locked.data.database.password); // null

// Load with unlocking — keyring fields are filled from the OS keyring
const unlocked = await config.load().unlock(KEYRING);
console.log(unlocked.data.database.password); // "secret"

// Partially update a single field
await config.patch({ theme: "light" }).run();

// Check whether the file exists
const present = await config.exists();
console.log(present); // true

// Delete the config file and wipe its keyring entries
await config.delete(KEYRING);
The config file is written to {AppConfig}/app.json. Adjust baseDir, options.dirName, and options.currentPath in the Configurate constructor to control the exact path — see Path resolution for details.

Complete working example

The snippet below is a self-contained module you can drop into any Tauri v2 project after completing the steps above.
src/lib/config.ts
import {
  BaseDirectory,
  Configurate,
  JsonProvider,
  defineConfig,
  keyring,
  optional,
} from "tauri-plugin-configurate-api";

const appSchema = defineConfig({
  theme: String,
  language: String,
  fontSize: optional(Number),
  database: {
    host: String,
    password: keyring(String, { id: "db-password" }),
  },
});

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

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

async function main() {
  // Create
  await config
    .create({
      theme: "dark",
      language: "en",
      database: { host: "localhost", password: "secret" },
    })
    .lock(KEYRING)
    .run();

  // Load (unlocked)
  const result = await config.load().unlock(KEYRING);
  console.log(result.data.theme);             // "dark"
  console.log(result.data.database.password); // "secret"

  // Patch
  await config.patch({ theme: "light" }).run();
}

main();

Next steps

Core concepts: Schema

Learn about defineConfig, keyring, optional, and array schemas

Core concepts: Providers

Choose and configure the right storage backend for your app

CRUD operations

Explore create, load, save, patch, delete, exists, and reset

Batch operations

Load, save, or patch multiple configs in a single IPC call

Build docs developers (and LLMs) love