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
- Open the Temps dashboard
- Click New Project
- Select your Git provider and repository
- Choose the branch to deploy
Temps detects your framework:
- Vite — if
viteis inpackage.jsondependencies - Create React App — if
react-scriptsis inpackage.jsondependencies
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;
Because variables are embedded at build time, changing an environment variable in the dashboard requires a new deployment. The variable is baked into the JavaScript bundle — there is no runtime injection for static sites.
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:
- Clones your repository
- Detects Vite (or CRA) and generates a Dockerfile
- Installs dependencies using the detected package manager
- Runs the build command (
vite buildorreact-scripts build) - Extracts the static files from the build container
- 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:
- Has no file extension
- 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 Modifiedresponses - Must-revalidate for
index.htmland 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:
- Variables use the correct prefix (
VITE_for Vite,REACT_APP_for CRA) - You triggered a new deployment after adding/changing the variable
- You are using the right API (
import.meta.envfor Vite,process.envfor 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.