Build Configuration

A complete reference for the build section of tauri.conf.json in Tauri v2 with React and Vite covering frontend paths, dev server URL, before hooks, watch folders and platform-specific options

The build configuration inside tauri.conf.json tells Tauri how to find your frontend code during development and how to compile it before bundling the final application. Without it, Tauri doesn't know where your React app lives, what command starts the dev server, or which folder holds the finished static files.

In a Tauri v2 project that uses Vite and React, the build section sits at the top level of the configuration file. It typically looks like this:

{
  "build": {
    "frontendDist": "../dist",
    "devUrl": "http://localhost:5173",
    "beforeDevCommand": "npm run dev",
    "beforeBuildCommand": "npm run build"
  }
}

Each field answers a specific question Tauri asks at a different stage of the workflow. The rest of this document walks through those fields, what they control, and the mistakes that most often trip up new users.

The Two Required Fields

Two fields are mandatory: frontendDist and devUrl. Tauri uses them in different contexts, but both are about locating your frontend.

frontendDist

This is the path to the directory that contains your built, production‑ready frontend assets—HTML, JavaScript, CSS, and any other static files Vite produces when you run npm run build. The path must be relative to the src-tauri directory.

Vite outputs its build to a dist folder at the root of the project by default. Since src-tauri is one level deeper, the correct relative path is "../dist".

{
  "build": {
    "frontendDist": "../dist"
  }
}

When you run tauri build, the CLI first looks at frontendDist, then executes whatever you have in beforeBuildCommand. After that command finishes, Tauri reads the files from this folder and inlines them into the final binary. If this path is wrong—or if the folder doesn't exist because the build command failed—the bundling step errors out.

Relative path confusion:

The path is always relative to src-tauri, not the project root. If you move dist inside src-tauri, change the field to "dist". The most common mistake is writing "../dist" when the actual folder is somewhere else, which causes a build failure with a missing directory error.

devUrl

When you run tauri dev, Tauri opens a native window and points its internal webview at the URL you specify here. This must be the exact address where your Vite development server is running.

Vite's default port is 5173, so the typical value is:

{
  "build": {
    "devUrl": "http://localhost:5173"
  }
}

If you change Vite's port in vite.config.ts, update this field to match. Tauri waits for the dev server to be ready before it tries to load anything, and it checks whether the server is up by polling this URL. If the URL is wrong, the window opens but the page stays blank.

How readiness detection works:

Tauri watches the output of your beforeDevCommand for a line that looks like a URL and also polls the given address. If your command doesn't print a recognizable address, you can still rely on polling—just make sure the devUrl matches exactly where the server actually binds.

Before Commands

These two fields let you inject shell commands right before Tauri starts its own work. They are what turns a manual two‑step process into a single tauri dev or tauri build invocation.

beforeDevCommand

The command that starts your Vite development server. Tauri runs this shell command before opening the application window in dev mode.

{
  "build": {
    "beforeDevCommand": "npm run dev"
  }
}

The npm script "dev" inside package.json typically maps to vite or vite --host. The important detail is that the command must keep running—Tauri will not kill it. When you stop tauri dev, the frontend dev server is stopped automatically.

If your dev server starts quickly and prints its URL, Tauri detects it within a few hundred milliseconds. If the command fails (non‑zero exit code), tauri dev aborts.

beforeBuildCommand

The command that compiles your frontend for production. Tauri runs it at the beginning of tauri build, before it touches the frontendDist folder.

{
  "build": {
    "beforeBuildCommand": "npm run build"
  }
}

Vite's build command generates static files into the dist folder. The exit code matters here: if it's non‑zero, Tauri stops the entire build pipeline. Make sure the script exits cleanly (code 0) when the build succeeds.

V1 field names will break the config:

In Tauri v1 the equivalent fields were called devPath and distDir. Using them in a v2 config will cause validation errors: Additional properties are not allowed ('devPath', 'distDir' were unexpected). If you see this, replace them with devUrl and frontendDist.

Additional Watch Folders

During tauri dev, Tauri watches your Rust source files for changes and rebuilds automatically. The frontend's own dev server (Vite) handles hot module replacement for JavaScript and CSS. But sometimes you have files outside those two scopes—shared configuration, a JSON schema, or assets from a monorepo package—that should also trigger a rebuild when they change.

additionalWatchFolders is an array of directory paths (relative to src-tauri) that Tauri will monitor alongside its normal watched paths.

{
  "build": {
    "additionalWatchFolders": ["../shared", "../config"]
  }
}

When any file inside those folders changes, Tauri recompiles the Rust backend. This is useful if your build.rs or custom Rust code reads from those locations at compile time.

Performance consideration:

Each added folder introduces filesystem watcher overhead. Only add directories that genuinely need to trigger a rebuild, not large folders like node_modules.

Remove Unused Commands

Tauri v2 allows you to strip command handlers that are never called from the frontend. This is an optimization that reduces binary size and attack surface.

Set removeUnusedCommands to true to enable it:

{
  "build": {
    "removeUnusedCommands": true
  }
}

At build time, the Tauri CLI scans your frontend code for invocations of invoke() or plugin APIs. Any Rust command that isn't referenced gets removed from the final binary.

Dynamic command invocation breaks this:

The analysis is static. If you construct command names at runtime from a string variable or call invoke with an expression that can't be statically resolved, the corresponding command will be stripped away even if you intended to use it. Only enable this when you can confirm all commands are referenced directly.

Windows‑Specific Build Option

On Windows, C++ applications sometimes depend on the Visual C++ Redistributable. Tauri lets you decide whether to include that runtime inside your executable or leave it as an external dependency.

The field build.windows.staticVCRuntime controls this:

{
  "build": {
    "windows": {
      "staticVCRuntime": true
    }
  }
}

When set to true (the default), the Microsoft Visual C++ Runtime is linked statically into your .exe. Users don't need to install anything extra, but the binary is slightly larger. When false, the runtime is expected to be present on the user's machine—if it isn't, the app won't launch. For distribution to end users, keeping it true is almost always the right choice.

A Full Working Example

Below is a complete tauri.conf.json build section for a React + Vite project, accompanied by the relevant package.json scripts it depends on. This is exactly what you'd have after scaffolding with npm create tauri-app@latest and choosing React with Vite.

{
  "productName": "my-tauri-app",
  "version": "0.1.0",
  "identifier": "com.example.my-tauri-app",
  "build": {
    "frontendDist": "../dist",
    "devUrl": "http://localhost:5173",
    "beforeDevCommand": "npm run dev",
    "beforeBuildCommand": "npm run build",
    "additionalWatchFolders": [],
    "removeUnusedCommands": false,
    "windows": {
      "staticVCRuntime": true
    }
  },
  "app": {
    "security": {
      "csp": null
    },
    "windows": [
      {
        "title": "My Tauri App",
        "width": 800,
        "height": 600
      }
    ]
  },
  "bundle": {
    "active": true,
    "targets": "all",
    "icon": [
      "icons/32x32.png",
      "icons/128x128.png",
      "icons/128x128@2x.png",
      "icons/icon.icns",
      "icons/icon.ico"
    ]
  },
  "plugins": {}
}

The matching npm scripts inside the project root's package.json look like this:

{
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "tauri": "tauri"
  }
}

The dev script starts Vite on port 5173 by default, which matches devUrl. The build script type‑checks the TypeScript code (tsc) then builds the production bundle into the dist folder, matching frontendDist.

When you run npm run tauri dev, the sequence is:

  1. Tauri reads beforeDevCommand, runs npm run dev.
  2. Vite starts and prints Local: http://localhost:5173/.
  3. Tauri detects the URL, opens the native window, and loads http://localhost:5173.

When you run npm run tauri build, the sequence is:

  1. beforeBuildCommand runs npm run build, producing files in dist/.
  2. Tauri reads the content of ../dist and embeds it into the Rust binary.
  3. The final executable and platform‑specific installers are created in src-tauri/target/release/bundle/.

Everything is working if you see this:

After a successful configuration, running tauri dev should open a native window showing your React app exactly as it appears in the browser. The terminal shows both the Vite output and Tauri's own log messages interleaved, confirming the pipeline is intact.

Common Mistakes in Build Configuration

Below are the errors that appear most often, how to recognize them, and how to fix them.

Using Tauri v1 property names. As mentioned earlier, devPath and distDir are not valid in v2. The Tauri CLI will complain about unexpected additional properties. Replace them with devUrl and frontendDist.

Wrong frontendDist path. If you see an error like Could not find frontend directory, double‑check that the path is relative to src-tauri. For a standard Vite setup, it's "../dist". If you moved the output folder via Vite's build.outDir option, adjust accordingly.

Missing beforeDevCommand. If you run tauri dev and the window stays white, the dev server probably never started. Without beforeDevCommand, Tauri doesn't know how to launch Vite. Add the correct npm script or direct command (e.g., "npx vite").

beforeBuildCommand exits with an error. If npm run build fails (TypeScript errors, missing files), Tauri stops. Fix the frontend build first, then rerun tauri build.

removeUnusedCommands strips a needed command. If a feature suddenly stops working in production but works in dev, you might be calling a command dynamically. Either disable the removal or restructure the invocation so it's statically analyzable.

Mismatched Vite port. If you change Vite's port in vite.config.ts but forget to update devUrl, Tauri will open the window and keep polling the wrong address until it times out. Keep them synchronized.

Summary

The build configuration is the glue between your React frontend and the Tauri Rust core. It ensures the dev server starts before the window opens, the production files land in the right place, and the final binary bundles everything cleanly. Once you understand the required fields and the two before‑hooks, the rest are optional optimizations you can reach for as your project grows.