Understanding Assets

Learn what assets are in a Tauri application, how they differ from resources, and how the frontend build produces the files included in your final binary.

When you open a Tauri app, a native window appears — but what you see inside is a web page. That page and everything it needs to render correctly (scripts, styles, images, fonts, and other files) must be shipped with your application. In Tauri, these shipped pieces are called assets.

Assets are the static files that form your application’s user interface. They are embedded directly into the final executable so that the webview can load them instantly, without any external network requests. Understanding what counts as an asset, how the build pipeline produces them, and how they differ from resources is the foundation for configuring any Tauri project correctly.

What Are Assets in a Tauri Application?

An asset in Tauri is any file that lives inside the frontend build output directory and is meant to be served to the webview. This includes the HTML entry point (typically index.html), JavaScript bundles, CSS stylesheets, images, fonts, JSON data files, and WebAssembly modules. From the webview’s perspective, these are just local files served by a custom protocol — but from your build tool’s perspective, they are the result of processing and bundling your source code.

Think of assets as the “finished product” of your frontend build step. When you write React components, import CSS, or reference an image, you are working with source files. Vite transforms all of that into a flat set of browser‑ready files inside a directory (often called dist). Tauri then takes that entire directory and packages it into the binary.

Assets are always embedded:

Assets are bundled directly into the executable at compile time. They are not separate files on disk after installation, and they are not meant to be modified by the user. This is what makes Tauri applications portable and self-contained.

Frontend Assets and the Build Pipeline

React components cannot be consumed by a browser or a webview in their raw form. TypeScript and JSX need to be compiled, imports need to be resolved, and assets like images need to be processed and given cache‑busting filenames. This job belongs to your frontend build tool.

In a React + Vite project, Vite reads your source code and produces an optimized set of static files. The output location is configured in vite.config.ts:

vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
  plugins: [react()],
  build: {
    outDir: 'dist',
  },
});

After running npm run build (or vite build), the dist folder will contain an index.html, hashed JavaScript and CSS files, and any imported images or other media. These are the frontend assets — the files Tauri expects to bundle.

Tauri needs to know where to find this output directory. The build.distDir field in src‑tauri/tauri.conf.json points to it, relative to the Tauri configuration file’s location:

src-tauri/tauri.conf.json
{
  "build": {
    "distDir": "../dist"
  }
}

When you run tauri build, the Tauri CLI will first build your frontend (if you are using the built‑in beforeBuildCommand) and then embed every file from the distDir into the final binary. During development, the frontend is served by Vite’s dev server, so assets are not yet bundled — Tauri proxies requests to the dev server instead.

A small React component that imports an image demonstrates how this pipeline includes everything automatically:

src/App.tsx
import logo from './assets/logo.png';
function App() {
  return (
    <div>
      <img src={logo} alt="Logo" />
    </div>
  );
}
export default App;

Vite sees the import logo from './assets/logo.png' statement, copies logo.png into the build output with a hashed filename (e.g., logo.a1b2c3d4.png), and replaces the import with the correct URL. The resulting JavaScript bundle references that hashed filename, and the image file itself ends up in the dist/assets folder. Tauri simply packages everything from dist — no extra configuration required.

Missing dist directory causes blank screen:

If the frontend has not been built before tauri build, or if distDir points to a non‑existent location, the final executable will contain no UI assets. The result is a blank window or a load error. Always ensure your frontend build completes successfully before packaging.

Assets vs Resources

A common point of confusion is the difference between assets and resources. Both terms appear in Tauri’s configuration and both involve files your application needs, but they serve fundamentally different purposes and are handled differently at build time.

Assets are the frontend UI files. They are embedded in the binary, served to the webview through a secure custom protocol (like tauri://localhost), and never appear as regular files on the user’s filesystem. The webview cannot access them through a standard file path, and neither can your Rust backend — they are only consumable as web resources loaded from that protocol.

Resources are external files that your app may need at runtime but that are not part of the user interface. Examples include configuration files that should remain user‑editable, external binaries (sidecars), or data files that must be read with filesystem APIs. Resources are specified in tauri.conf.json under bundle.resources and are placed in a resource directory alongside the executable at installation time. They remain as individual files on disk, accessible both from Rust (using tauri::api::path) and from the frontend (via Tauri’s asset protocol with a special asset:// prefix, if configured).

PropertyAssetsResources
PurposeFrontend UI filesExternal runtime files
Stored asEmbedded in binarySeparate files on disk
Access from RustNot directly (protocol only)Yes, via path resolver
Access from webviewtauri://localhost / custom protocolasset:// (if allowed)
Modifiable by userNoYes, if permissions allow
Configured inbuild.distDirbundle.resources

Mixing assets and resources causes unexpected failures:

Putting a large data file inside the dist folder will embed it as an asset — it becomes part of the binary and cannot be changed without a reinstall. Conversely, expecting a resource to be served as a web URL without the proper protocol configuration will result in a 404. Always decide whether a file needs to be embedded (asset) or remain accessible on disk (resource) before you place it in your project.

How Tauri Serves Assets at Runtime

Once embedded, assets are not accessed through a file:// URL. Tauri registers a custom protocol that intercepts requests to a specific origin (like https://tauri.localhost or tauri://localhost) and serves the embedded files directly from memory. This approach provides two important benefits:

  • Security – The webview never touches the real filesystem. Even if a malicious script attempts to read local files using a file:// path, it cannot reach the embedded assets, and the protocol is sandboxed.
  • CORS avoidance – Since everything is served from the same origin, you never encounter cross‑origin issues when loading images, fonts, or dynamic imports. All assets appear to come from the same secure origin.

During production, the webview loads the entry point with something like https://tauri.localhost/index.html. That HTML then references /assets/index.a1b2c3.js, /assets/style.d4e5f6.css, and any imported images — all resolved relative to the custom origin and served directly from the binary.

Everything is working:

If you run tauri build, install the resulting package, and see your React application functioning exactly as it did in development, the asset pipeline is correctly configured. All frontend files are embedded, the protocol is serving them, and no resources are missing.

A Brief Look at Static Assets and the Public Directory

For files that should be included verbatim — without being processed by Vite’s bundler — the public directory exists. Anything placed inside public/ (at the root of your frontend project) gets copied directly into the dist output. This is where you put a favicon.ico, a robots.txt, or any file that must retain its exact name and path.

For example, placing an image at public/images/icon.png makes it available in the built app as /images/icon.png. Tauri will embed it because it ends up in the dist folder. The mental model is simple: public is a pass‑through folder for assets that do not need bundling.

Common Mistakes and Misunderstandings

Even experienced developers sometimes stumble over the asset model. Here are the most frequent pitfalls:

  • Forgetting to set build.distDir correctly – The path is relative to the src‑tauri directory. A wrong path leads to a binary with no UI. Double‑check that it matches your frontend’s output folder.
  • Assuming assets can be read by Rust with std::fs – The embedded files exist only in memory and are not accessible as filesystem paths. If your Rust code needs to read a file, it should be a resource, not an asset.
  • Placing too many large files in dist – Every megabyte of assets increases the binary size and load time. If you have large media files that could be fetched from a server or loaded on demand, consider keeping them external to the binary.
  • Confusing the dev server with the bundled result – In development, everything works because Vite handles serving. The final test is always tauri build. Behavior that relies on dev‑server‑specific features (like hot module replacement) will not exist in the built assets.

Understanding what assets are and where they come from sets the stage for every configuration decision you make about your Tauri application’s frontend.