# How to Build a Desktop Application from NextChat Using Tauri

> Learn to build a desktop app for NextChat using Tauri. Package your Next.js web app into native Windows macOS and Linux binaries with this powerful Rust framework.

- Repository: [NextChat/NextChat](https://github.com/ChatGPTNextWeb/NextChat)
- Tags: how-to-guide
- Published: 2026-02-28

---

**NextChat can be compiled into native desktop binaries for Windows, macOS, and Linux using the integrated Tauri framework that wraps the Next.js web interface in a Rust-powered runtime.**

NextChat (ChatGPTNextWeb/NextChat) ships with a complete Tauri scaffold that enables packaging the React/Next.js frontend as a native application. This guide explains how to build a desktop application from NextChat using Tauri by examining the repository's Rust backend, custom HTTP streaming commands, and build configuration.

## Architecture Overview

The Tauri implementation consists of five integrated layers that bridge the web UI with native system capabilities.

- **Web UI (Next.js)**: The original NextChat SPA located in `app/...` builds into static files that Tauri serves as the frontend.
- **Tauri Configuration**: [`src-tauri/tauri.conf.json`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/src-tauri/tauri.conf.json) declares build commands, output directories, window settings, bundling options, and allowed APIs.
- **Rust Runtime**: [`src-tauri/src/main.rs`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/src-tauri/src/main.rs) initializes the Tauri process, registers custom commands, and runs the event loop.
- **Custom Command**: [`src-tauri/src/stream.rs`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/src-tauri/src/stream.rs) implements `stream_fetch`, a Rust-side HTTP client that streams LLM API responses while bypassing browser CORS restrictions.
- **TypeScript Bridge**: [`app/utils/stream.ts`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/utils/stream.ts) detects the Tauri environment and substitutes standard `fetch` calls with invocations to the Rust command.

### Request Flow for LLM APIs

When the UI initiates an HTTP request, the data flows through a specialized pipeline that keeps network operations outside the browser sandbox.

1. The UI calls `fetch(url, options)`, which detects `window.__TAURI__` and forwards the request via `window.__TAURI__.invoke("stream_fetch", ...)`.
2. The Rust command in [`src-tauri/src/stream.rs`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/src-tauri/src/stream.rs) creates a `reqwest::Client`, sends the request, and emits raw byte chunks back to the web view using the Tauri event `"stream-response"` with `ChunkPayload` structs.
3. The TypeScript bridge listens for these events, writes each chunk into a `TransformStream`, and resolves a standard `Response` object once the stream completes via an `EndPayload` signal.

This architecture allows the desktop application to perform arbitrary HTTP requests and stream large responses without browser security limitations.

## Prerequisites and Environment Setup

Before building the desktop application, ensure your development environment meets the repository requirements.

- **Node.js**: Version 18 or higher (as specified in the repository README).
- **Yarn**: Version 1.x (the project uses Yarn for script execution).
- **Rust Toolchain**: Install the stable channel via `rustup` to compile the Tauri backend.
- **Tauri CLI**: The `@tauri-apps/cli` package is already listed as a dev dependency, so manual installation is optional.

## Build Steps

Follow these steps to compile NextChat into distributable desktop binaries.

### 1. Install Dependencies

Clone the repository and install Node.js packages using Yarn.

```bash
git clone https://github.com/ChatGPTNextWeb/NextChat.git
cd NextChat
yarn install

```

### 2. Export Static Web Assets

Tauri requires the built UI in the `../out` directory relative to `src-tauri`. The `beforeBuildCommand` in [`tauri.conf.json`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/tauri.conf.json) runs `yarn export` automatically, but you can generate the assets manually.

```bash
yarn export

```

This command builds the Next.js application and writes static files to `./out`.

### 3. Run Development Mode with Hot Reload

Use the `app:dev` script to start the Next.js development server and launch Tauri in watch mode simultaneously.

```bash
yarn app:dev

```

- The Next.js dev server runs on `http://localhost:3000` as defined in [`tauri.conf.json`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/tauri.conf.json).
- Tauri loads this URL directly instead of the static `out` folder, enabling live code changes without rebuilding the Rust binary.

### 4. Create Production Builds

Generate platform-specific installers and executables using the `app:build` script.

```bash
yarn app:build

```

This sequence executes `yarn mask` (for prompt template compilation) followed by `yarn tauri build`. The Tauri CLI reads [`src-tauri/tauri.conf.json`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/src-tauri/tauri.conf.json) and creates native bundles for Windows (`.msi`), macOS (`.dmg`), and Linux (`.AppImage`).

### 5. Locate Output Binaries

After a successful build, find the distributable files in `src-tauri/target/release/bundle/`:

- **Windows**: `src-tauri/target/release/bundle/msi/NextChat_2.16.1_x64_en-US.msi`
- **macOS**: `src-tauri/target/release/bundle/dmg/NextChat_2.16.1_x64.dmg`
- **Linux**: `src-tauri/target/release/bundle/appimage/NextChat_2.16.1_amd64.AppImage`

## Key Implementation Files

Understanding these core files helps when customizing the desktop application or extending its native capabilities.

### Tauri Configuration

The [`src-tauri/tauri.conf.json`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/src-tauri/tauri.conf.json) file controls the build pipeline and security settings. It defines `beforeBuildCommand` as `yarn export`, sets the `distDir` to `../out`, and configures the `allowlist` for HTTP requests, file system access, and notifications.

```json
{
  "build": {
    "beforeBuildCommand": "yarn export",
    "beforeDevCommand": "yarn export:dev",
    "devPath": "http://localhost:3000",
    "distDir": "../out"
  },
  "tauri": {
    "allowlist": {
      "http": { "all": true, "request": true, "scope": ["https://*", "http://*"] }
    },
    "bundle": {
      "category": "DeveloperTool",
      "icon": ["icons/32x32.png", "icons/128x128.png", "icons/icon.icns"]
    }
  }
}

```

### The stream_fetch Rust Command

Located in [`src-tauri/src/stream.rs`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/src-tauri/src/stream.rs), this command handles HTTP requests natively using the `reqwest` crate. It accepts method, URL, headers, and body parameters, then streams the response back to the frontend via Tauri events.

```rust
#[tauri::command]
pub async fn stream_fetch(
    window: tauri::Window,
    method: String,
    url: String,
    headers: HashMap<String, String>,
    body: Vec<u8>,
) -> Result<StreamResponse, String> {
    let client = reqwest::Client::new();
    let response = client.request(method.parse()?, url.parse()?)
        .headers(convert_headers(headers)?)
        .body(body)
        .send()
        .await;

    match response {
        Ok(res) => {
            let request_id = generate_id();
            let status = res.status().as_u16();
            
            // Stream chunks back to the webview
            tauri::async_runtime::spawn(async move {
                let mut stream = res.bytes_stream();
                while let Some(chunk) = stream.next().await {
                    if let Ok(bytes) = chunk {
                        window.emit("stream-response", ChunkPayload {
                            request_id: request_id.clone(),
                            chunk: bytes.to_vec()
                        }).ok();
                    }
                }
                window.emit("stream-response", EndPayload {
                    request_id: request_id.clone(),
                    status: 0
                }).ok();
            });

            Ok(StreamResponse {
                request_id,
                status,
                status_text: "OK".into(),
                headers: extract_headers(&res),
            })
        }
        Err(err) => Err(err.to_string()),
    }
}

```

### The TypeScript Bridge

The [`app/utils/stream.ts`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/app/utils/stream.ts) file provides a drop-in replacement for the standard `fetch` API. When `window.__TAURI__` is present, it invokes `stream_fetch` and reconstructs a web-standard `Response` object from the streamed chunks.

```typescript
if (window.__TAURI__) {
  const { method = "GET", headers = {}, body } = options || {};
  
  return window.__TAURI__.invoke("stream_fetch", {
    method: method.toUpperCase(),
    url,
    headers,
    body: typeof body === "string" 
      ? Array.from(new TextEncoder().encode(body)) 
      : [],
  }).then((res: StreamResponse) => {
    const { request_id, status, status_text, headers } = res;
    setRequestId?.(request_id);
    
    // Reassemble streaming chunks into a Response
    return new Response(readableStream, { 
      status, 
      statusText: status_text, 
      headers 
    });
  });
}

```

## Summary

- NextChat uses Tauri to wrap the Next.js frontend as a native desktop application for Windows, macOS, and Linux.
- The `stream_fetch` Rust command in [`src-tauri/src/stream.rs`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/src-tauri/src/stream.rs) bypasses browser CORS limitations by executing HTTP requests in the native runtime.
- Run `yarn app:dev` to launch the application in development mode with hot-reload support on `localhost:3000`.
- Execute `yarn app:build` to generate production binaries including `.msi`, `.dmg`, and `.AppImage` packages in `src-tauri/target/release/bundle/`.
- Customize window behavior, security policies, and branding by editing [`src-tauri/tauri.conf.json`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/src-tauri/tauri.conf.json).

## Frequently Asked Questions

### What is Tauri and why does NextChat use it?

**Tauri is a Rust-based framework for building desktop applications using web technologies.** NextChat uses Tauri to package the existing Next.js web interface as a native application without rewriting the frontend. This approach provides native performance, smaller bundle sizes than Electron, and full access to system APIs while maintaining the web-based codebase.

### How does the stream_fetch command handle HTTP requests?

**The `stream_fetch` command executes HTTP requests using Rust's `reqwest` library instead of the browser's fetch API.** It accepts request parameters from the frontend, sends the request from the native Rust context, and streams response chunks back via Tauri events. This design allows the application to bypass CORS restrictions and handle large streaming responses from LLM APIs efficiently.

### Where are the compiled desktop binaries located?

**Production builds appear in `src-tauri/target/release/bundle/` under platform-specific subdirectories.** Windows installers use the `msi/` folder, macOS disk images are in `dmg/`, and Linux AppImage files are located in `appimage/`. The filenames follow the pattern `NextChat_[version]_[arch].[ext]` (for example, `NextChat_2.16.1_x64_en-US.msi`).

### Can I customize the desktop app branding and window settings?

**Yes, modify [`src-tauri/tauri.conf.json`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/src-tauri/tauri.conf.json) to change the product name, icons, window dimensions, and security policies.** The `package.productName` field controls the application title, while the `tauri.bundle` section configures installer metadata and icon paths. For advanced customizations such as native menu bars or tray icons, extend the Rust code in [`src-tauri/src/main.rs`](https://github.com/ChatGPTNextWeb/NextChat/blob/main/src-tauri/src/main.rs).