Deploy a React App

This tutorial walks you through deploying a React single-page application on Temps. Whether you use Vite or Create React App, your static build is served directly from the proxy with SPA routing, gzip compression, and cache headers — no container running at runtime.


What you will build

  • A React app deployed as a static site
  • SPA routing that works on all paths
  • Environment variables embedded at build time
  • Automatic HTTPS and deployment on every push

Time required: 5 minutes.

Prerequisites:

  • A running Temps instance (install guide)
  • A React app (Vite or CRA) in a Git repository

Step 1: Prepare your app

Vite (recommended)

Verify your package.json has a build script:

package.json

{
  "name": "my-react-app",
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  },
  "dependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
    "vite": "^7.0.0",
    "@vitejs/plugin-react": "^4.0.0"
  }
}

No other configuration is needed. Temps detects Vite and knows the output directory is dist/.

Create React App

CRA projects work the same way. Temps detects react-scripts and uses build/ as the output directory:

package.json

{
  "name": "my-cra-app",
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build"
  },
  "dependencies": {
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "react-scripts": "5.0.1"
  }
}

Step 2: Create a project

  1. Open the Temps dashboard
  2. Click New Project
  3. Select your Git provider and repository
  4. Choose the branch to deploy

Temps detects your framework:

  • Vite — if vite is in package.json dependencies
  • Create React App — if react-scripts is in package.json dependencies

Step 3: Configure environment variables

React apps embed environment variables in the JavaScript bundle at build time. Variables must use the correct prefix to be included:

Vite

Variables must start with VITE_:

VITE_API_URL=https://api.example.com
VITE_PUBLIC_KEY=pk_live_abc123

Access them in your code:

const apiUrl = import.meta.env.VITE_API_URL;

Create React App

Variables must start with REACT_APP_:

REACT_APP_API_URL=https://api.example.com
REACT_APP_PUBLIC_KEY=pk_live_abc123

Access them in your code:

const apiUrl = process.env.REACT_APP_API_URL;

Variables without the prefix

Variables without the required prefix (e.g. DATABASE_URL, SECRET_KEY) are available during the Docker build step but are NOT included in the client bundle. Use this for build-time secrets that should not be exposed to users.


Step 4: Deploy

Push a commit or click Deploy. Temps:

  1. Clones your repository
  2. Detects Vite (or CRA) and generates a Dockerfile
  3. Installs dependencies using the detected package manager
  4. Runs the build command (vite build or react-scripts build)
  5. Extracts the static files from the build container
  6. Serves them directly from the Temps proxy

A typical React build takes 30-60 seconds.


Step 5: Verify

Open the generated URL. Your React app should load and client-side routing should work on all paths.

Test a few things:

  • Navigate to different routes — they should work without 404 errors
  • Refresh the page on a deep route (e.g. /dashboard/settings) — it should still work
  • Check the browser console for environment variable issues

SPA routing

Single-page applications handle routing in the browser with React Router, TanStack Router, or similar. When a user navigates to /about, there is no about.html file on disk — the JavaScript router handles it.

Temps handles this automatically. When a request arrives for a path that:

  1. Has no file extension
  2. Does not match a file on disk

The proxy serves index.html, and your client-side router takes over.

You do not need _redirects, vercel.json, netlify.toml, or any rewrite configuration.

React Router example

import { BrowserRouter, Routes, Route } from 'react-router';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/dashboard/*" element={<Dashboard />} />
      </Routes>
    </BrowserRouter>
  );
}

Connecting to a backend

React apps typically call a separate API. Set the API URL as an environment variable:

VITE_API_URL=https://api.your-domain.com

src/api.ts

const API_URL = import.meta.env.VITE_API_URL;

export async function fetchUsers() {
  const response = await fetch(`${API_URL}/api/users`);
  return response.json();
}

If your API is also deployed on Temps, both the frontend and API can share the same base domain with different subdomains (e.g. app.example.com and api.example.com). Configure CORS on the API to allow requests from the frontend domain.


Custom build configuration

Override the defaults in .temps.yaml:

.temps.yaml

build:
  install_command: pnpm install --frozen-lockfile
  build_command: pnpm run build:production
  output_dir: dist

This is useful when:

  • You have multiple build scripts (e.g. build:staging, build:production)
  • Your output directory is non-standard
  • You want to use a specific package manager command

Performance

Temps applies these optimizations to static sites automatically:

  • Gzip compression for HTML, CSS, JS, JSON, and SVG files over 1 KB
  • Immutable cache headers (max-age=31536000, immutable) for hashed assets in /assets/, /static/, and /_next/static/
  • ETag support for cache validation with 304 Not Modified responses
  • Must-revalidate for index.html and non-hashed files so new deployments take effect immediately

To maximize these benefits, ensure your build tool produces hashed filenames. Vite does this by default (e.g. assets/index-a1b2c3.js). CRA also hashes by default.


Troubleshooting

Blank page

The build output is missing or index.html is not in the expected directory. Check the build logs to see where files are written. If your Vite config uses build.outDir, set the same value in .temps.yaml:

build:
  output_dir: out  # Must match vite.config.ts build.outDir

Environment variables are undefined

Check that:

  1. Variables use the correct prefix (VITE_ for Vite, REACT_APP_ for CRA)
  2. You triggered a new deployment after adding/changing the variable
  3. You are using the right API (import.meta.env for Vite, process.env for CRA)

Assets return 404

If images or other assets in public/ are not loading, check that the base option in vite.config.ts is set correctly. For most deployments, leave it as the default (/):

vite.config.ts

export default defineConfig({
  base: '/',  // Default — correct for most deployments
  plugins: [react()],
});

Setting base to a subdirectory (e.g. /app/) changes the URL prefix for all assets.


What to explore next

Add a custom domain Environment variable management Add analytics to your app Deploy a backend API

Was this page helpful?