# Deploy Vite (React) to thin.host

Build a Vite + React app to dist/ and put it live on thin.host from your agent or a curl call. thin.host serves the static files and gives you a live URL right away. Your agent can do every step for you through the thin.host MCP server, or you can run one `curl` command.

## At a glance

| Step | Vite (React) |
|---|---|
| Build command | `npm run build` |
| Folder to deploy | `dist/` |
| Preview build (served at `/s/my-site/`) | `npm run build -- --base=/s/my-site/` |
| Deploy | `curl -F file=@site.zip -H "Authorization: Bearer $THINHOST_API_KEY" https://thin.host/v1/websites/archive` |
| Live URL | `https://thin.host/s/my-site/`, or your own domain |

## Before you start

- **Requires:** A current Node.js LTS release.
- **A thin.host account.** [Sign up](https://thin.host/signup), then create an API key under Settings (for `curl` and the local MCP server), or connect your agent with OAuth as shown in [Quickstart (MCP)](https://thin.host/docs/quickstart-mcp).

## Create a project (skip if you have one)

```bash
npm create vite@latest my-app -- --template react
```

## Set the base path

Until a domain points at it, a thin.host site lives under a sub-path: `https://thin.host/s/<slug>/`. Vite rewrites asset URLs with its base option, which you can pass on the command line, so no config change is needed. If you use React Router, give the router the same base so routes resolve under the preview path.

```javascript
// src/main.jsx
<BrowserRouter basename={import.meta.env.BASE_URL}>
  <App />
</BrowserRouter>
```

| Where readers will look | Build with |
|---|---|
| The preview URL `https://thin.host/s/my-site/` | `npm run build -- --base=/s/my-site/` |
| A custom domain, or a free `<name>.thin.host` subdomain | `npm run build` (base path `/`) |

Pick the slug (`my-site` here) before you build the preview, and pass the same slug when you deploy.

## Build

```bash
npm run build
```

The finished site is in `dist/`. That folder is what you deploy.

## Deploy from your agent (MCP)

Connect thin.host to Claude Code, Claude Desktop, Cursor, VS Code, Windsurf, Codex or ChatGPT once ([setup for each client](https://thin.host/docs/quickstart-mcp)), then ask:

> Build this Vite (React) project for thin.host with the slug my-site and publish the dist/ folder.

With the local server (`uvx thinhost-mcp`) the agent runs the build and calls `publish_website` with the absolute path of `dist/`; large builds are split across requests automatically. Later changes go out with `update_website` and `sync=true`, which also removes files the new build dropped.

The hosted connector (`https://thin.host/mcp`, used by claude.ai and ChatGPT) cannot read your disk. From a coding agent it uploads with the REST call below; otherwise it publishes from a public link to a zip or HTML file with `publish_website_from_url`.

## Deploy with curl

```bash
cd dist && zip -r ../site.zip . && cd -
curl -F file=@site.zip -F title="My Vite (React) site" -F slug=my-site \
  -H "Authorization: Bearer $THINHOST_API_KEY" \
  https://thin.host/v1/websites/archive
```

The JSON response includes `url`, the live address (`https://thin.host/s/my-site/`). To redeploy, upload the new zip to the site's archive endpoint; pages and assets missing from it are removed:

```bash
curl -F file=@site.zip \
  -H "Authorization: Bearer $THINHOST_API_KEY" \
  https://thin.host/v1/websites/my-site/archive
```

A single upload can be up to 30 MB. See the [REST API reference](https://thin.host/docs/api) for every field.

## Add a custom domain

1. Open the site in the [dashboard](https://thin.host/dashboard) and add your hostname under **Custom Domains**, for example `www.example.com`. A `<name>.thin.host` subdomain is free and works immediately.
2. Create the DNS record the dashboard shows: a `CNAME` pointing at `cname.thin.host`. TLS is issued automatically once DNS resolves.
3. Rebuild with the base path `/` (`npm run build`) and redeploy.

Pages on a custom domain get a canonical tag, a generated `robots.txt` and a `sitemap.xml`.

## Vite (React) notes

- Hard-coded '/logo.png' strings in JSX are not rewritten; build runtime URLs from import.meta.env.BASE_URL.
- Client-side routes are not files. thin.host serves your 404.html for unknown paths, so copying dist/index.html to dist/404.html makes deep links load the app (with a 404 status), or use hash routing.

## FAQ

**Deep links to my React routes return a 404 page. What do I do?**

Copy dist/index.html to dist/404.html before zipping. thin.host serves 404.html for any path that is not a file, so the app boots and the router takes over. The response status stays 404, which matters only for crawlers; prerender pages you want indexed.

**Why does the preview URL load without styles or scripts?**

The site is served under /s/<slug>/ until a custom domain points at it, so a build that references /assets/... asks the thin.host root for files that live under the sub-path. Build with the base path set to /s/<slug>/ for the preview, or map a domain (a free <name>.thin.host subdomain works) and build with base /.

**How do I redeploy?**

Rebuild, zip the output folder again and POST it to /v1/websites/<slug>/archive, or ask your agent to run update_website with sync=true. Sync removes files the new build no longer contains, so old hashed bundles do not pile up.

**How large can a site be?**

A single archive upload can be up to 30 MB. The local MCP server (uvx thinhost-mcp) reads the build folder directly and splits large sites across requests, up to 150 MB.

## Sources

Commands and options checked against the official Vite (React) docs on 2026-10-14:

- [Vite: Deploying a static site](https://vite.dev/guide/static-deploy)
- [Vite: Public base path](https://vite.dev/guide/build#public-base-path)
- [React Router: BrowserRouter basename](https://reactrouter.com/api/declarative-routers/BrowserRouter)

Other frameworks: [all deploy guides](https://thin.host/guides). Coming from another host? [Compare thin.host](https://thin.host/compare).

---
Canonical page: https://thin.host/guides/deploy-vite-react
