Skip to content

Deploying a React App

React is a powerful, flexible JavaScript library for building user interfaces with reusable components and efficient rendering. With React, you can create dynamic, interactive web applications that respond instantly to user input, manage complex application state elegantly, and scale from small projects to massive enterprise systems. React’s component-based architecture, powerful hooks ecosystem, and vast community make it the most popular choice for modern web development. Whether you’re building single-page applications (SPAs), progressive web apps (PWAs), or complex dashboards, React provides the tools and patterns needed for success.

This comprehensive guide walks through deploying a React application to Klutch.sh using either Nixpacks (automatic zero-configuration deployment) or a Dockerfile (manual container control). You’ll learn how to set up a React project with Create React App or Vite, create and compose components, manage state with hooks and Context API, integrate with external APIs, optimize builds, configure environment variables, implement security best practices, set up monitoring, configure custom domains, and troubleshoot common issues. By the end of this guide, you’ll have a production-ready React application running on Klutch.sh’s global infrastructure with automatic HTTPS, optimized performance, and reliable hosting.

Prerequisites

  • Node.js & npm (version 16+, required) – Download Node.js
  • Git installed locally and a GitHub account (Klutch.sh uses GitHub as the only git source)
  • Klutch.sh account with access to the dashboard at klutch.sh/app
  • Basic knowledge of JavaScript, HTML, and CSS
  • Text editor or IDE for code editing (VS Code recommended)

Getting Started: Create a React App

1. Create a New React Project

Create a new React application using Vite (recommended for fast development) or Create React App:

Using Vite (recommended):

Terminal window
npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install

Using Create React App:

Terminal window
npx create-react-app my-react-app
cd my-react-app

2. Project Structure

A typical React application structure looks like:

my-react-app/
├── src/
│ ├── components/
│ │ ├── Header.jsx
│ │ ├── Footer.jsx
│ │ ├── Sidebar.jsx
│ │ ├── Button.jsx
│ │ └── Modal.jsx
│ ├── pages/
│ │ ├── Home.jsx
│ │ ├── Dashboard.jsx
│ │ ├── Profile.jsx
│ │ └── Settings.jsx
│ ├── hooks/
│ │ ├── useApi.js
│ │ ├── useFetch.js
│ │ ├── useLocalStorage.js
│ │ └── useAuth.js
│ ├── context/
│ │ ├── AuthContext.js
│ │ └── ThemeContext.js
│ ├── services/
│ │ ├── api.js
│ │ ├── auth.js
│ │ └── storage.js
│ ├── styles/
│ │ ├── index.css
│ │ ├── App.css
│ │ └── components.css
│ ├── utils/
│ │ ├── validation.js
│ │ ├── formatting.js
│ │ └── helpers.js
│ ├── App.jsx
│ ├── main.jsx
│ └── index.css
├── public/
│ ├── index.html
│ ├── favicon.ico
│ └── manifest.json
├── .env.example
├── package.json
├── vite.config.js
├── README.md
└── Dockerfile

3. Install and Run Locally

Install dependencies and start the development server:

Terminal window
npm install
npm run dev

Your React app will be available at http://localhost:5173 (Vite) or http://localhost:3000 (Create React App). The development server provides hot module replacement (HMR) for instant updates as you code.

4. Create Your First Component

Create src/components/Header.jsx:

import React from 'react';
import './Header.css';
export function Header({ title = 'My React App' }) {
return (
<header className="header">
<div className="container">
<h1>{title}</h1>
<p>Deployed on Klutch.sh</p>
</div>
</header>
);
}

Create src/components/Header.css:

.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 2rem;
text-align: center;
margin-bottom: 2rem;
}
.header h1 {
margin: 0;
font-size: 2.5rem;
}
.header p {
margin: 0.5rem 0 0;
font-size: 1.1rem;
}

5. Create a Counter Component with Hooks

Create src/components/Counter.jsx:

import React, { useState } from 'react';
import './Counter.css';
export function Counter() {
const [count, setCount] = useState(0);
const [history, setHistory] = useState([]);
const increment = () => {
const newCount = count + 1;
setCount(newCount);
setHistory([...history, newCount]);
};
const decrement = () => {
const newCount = count - 1;
setCount(newCount);
setHistory([...history, newCount]);
};
const reset = () => {
setCount(0);
setHistory([]);
};
return (
<div className="counter">
<h2>Counter Demo</h2>
<div className="counter-display">{count}</div>
<div className="counter-buttons">
<button onClick={increment} className="btn btn-success">
Increment
</button>
<button onClick={decrement} className="btn btn-danger">
Decrement
</button>
<button onClick={reset} className="btn btn-secondary">
Reset
</button>
</div>
{history.length > 0 && (
<div className="history">
<h3>History</h3>
<p>{history.join(', ')}</p>
</div>
)}
</div>
);
}

Create src/components/Counter.css:

.counter {
padding: 2rem;
border: 1px solid #ddd;
border-radius: 8px;
text-align: center;
margin-bottom: 2rem;
background: #f9f9f9;
}
.counter-display {
font-size: 3rem;
font-weight: bold;
color: #667eea;
margin: 1rem 0;
}
.counter-buttons {
display: flex;
gap: 1rem;
justify-content: center;
margin: 1.5rem 0;
}
.btn {
padding: 0.75rem 1.5rem;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
transition: opacity 0.3s ease;
}
.btn:hover {
opacity: 0.8;
}
.btn-success {
background-color: #27ae60;
color: white;
}
.btn-danger {
background-color: #e74c3c;
color: white;
}
.btn-secondary {
background-color: #95a5a6;
color: white;
}
.history {
margin-top: 1.5rem;
padding-top: 1.5rem;
border-top: 1px solid #ddd;
}
.history h3 {
margin-top: 0;
}

6. Create a Custom Hook for API Calls

Create src/hooks/useApi.js:

import { useState, useEffect } from 'react';
export function useApi(url, options = {}) {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const response = await fetch(url, {
...options,
headers: {
'Content-Type': 'application/json',
...options.headers
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
setData(result);
setError(null);
} catch (err) {
setError(err.message);
setData(null);
} finally {
setLoading(false);
}
};
if (url) {
fetchData();
}
}, [url]);
return { data, loading, error };
}

7. Create a Context for Global State

Create src/context/AuthContext.js:

import React, { createContext, useState, useContext } from 'react';
const AuthContext = createContext();
export function AuthProvider({ children }) {
const [user, setUser] = useState(null);
const [isAuthenticated, setIsAuthenticated] = useState(false);
const [loading, setLoading] = useState(false);
const login = async (email, password) => {
setLoading(true);
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
const data = await response.json();
if (response.ok) {
setUser(data.user);
setIsAuthenticated(true);
localStorage.setItem('token', data.token);
}
} catch (error) {
console.error('Login failed:', error);
} finally {
setLoading(false);
}
};
const logout = () => {
setUser(null);
setIsAuthenticated(false);
localStorage.removeItem('token');
};
return (
<AuthContext.Provider value={{ user, isAuthenticated, login, logout, loading }}>
{children}
</AuthContext.Provider>
);
}
export function useAuth() {
return useContext(AuthContext);
}

8. Create API Service Layer

Create src/services/api.js:

const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:3000';
export const api = {
get: async (endpoint) => {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token') || ''}`
}
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
return response.json();
},
post: async (endpoint, data) => {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token') || ''}`
},
body: JSON.stringify(data)
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
return response.json();
},
put: async (endpoint, data) => {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${localStorage.getItem('token') || ''}`
},
body: JSON.stringify(data)
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
return response.json();
},
delete: async (endpoint) => {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${localStorage.getItem('token') || ''}`
}
});
if (!response.ok) throw new Error(`API error: ${response.status}`);
return response.json();
}
};

9. Update App.jsx

Update src/App.jsx:

import React, { useState } from 'react';
import { Header } from './components/Header';
import { Counter } from './components/Counter';
import { Footer } from './components/Footer';
import './App.css';
function App() {
const [activeTab, setActiveTab] = useState('home');
return (
<div className="App">
<Header title="React on Klutch.sh" />
<nav className="navbar">
<div className="container">
<button
className={`nav-btn ${activeTab === 'home' ? 'active' : ''}`}
onClick={() => setActiveTab('home')}
>
Home
</button>
<button
className={`nav-btn ${activeTab === 'demo' ? 'active' : ''}`}
onClick={() => setActiveTab('demo')}
>
Demo
</button>
<button
className={`nav-btn ${activeTab === 'about' ? 'active' : ''}`}
onClick={() => setActiveTab('about')}
>
About
</button>
</div>
</nav>
<main className="container">
{activeTab === 'home' && (
<section>
<h2>Welcome to React</h2>
<p>React is a JavaScript library for building user interfaces with reusable components.</p>
<ul>
<li>⚛️ Component-based architecture</li>
<li>🚀 Fast and efficient rendering</li>
<li>🎯 Powerful state management</li>
<li>🔗 Large ecosystem and community</li>
</ul>
</section>
)}
{activeTab === 'demo' && (
<section>
<Counter />
</section>
)}
{activeTab === 'about' && (
<section>
<h2>About This App</h2>
<p>This React application is deployed on Klutch.sh for fast, reliable hosting.</p>
<p>It demonstrates key React concepts including:</p>
<ul>
<li>Functional components and hooks</li>
<li>State management with useState</li>
<li>Component composition</li>
<li>CSS styling</li>
<li>Navigation between views</li>
</ul>
</section>
)}
</main>
<Footer />
</div>
);
}
export default App;

Update src/App.css:

.App {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 2rem 1rem;
flex: 1;
}
.navbar {
background-color: #f5f5f5;
border-bottom: 1px solid #ddd;
padding: 1rem 0;
margin-bottom: 2rem;
}
.navbar .container {
display: flex;
gap: 1rem;
padding: 0 1rem;
margin-bottom: 0;
}
.nav-btn {
padding: 0.75rem 1.5rem;
background-color: white;
border: 1px solid #ddd;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
font-size: 1rem;
}
.nav-btn:hover {
background-color: #f9f9f9;
}
.nav-btn.active {
background-color: #667eea;
color: white;
border-color: #667eea;
}
main h2 {
color: #333;
margin-top: 0;
}
main ul {
list-style: none;
padding: 0;
}
main ul li {
padding: 0.5rem 0;
font-size: 1.1rem;
}
@media (max-width: 768px) {
.container {
padding: 1rem;
}
.navbar .container {
flex-wrap: wrap;
}
.nav-btn {
flex: 1;
min-width: 100px;
}
}

10. Create Environment Variables

Create .env.example:

REACT_APP_API_URL=http://localhost:3000
REACT_APP_APP_NAME=My React App
REACT_APP_ANALYTICS_ID=
REACT_APP_ENABLE_DEBUG=false

11. Configure Build

Update vite.config.js (if using Vite):

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
port: 5173,
strictPort: false,
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true
}
}
},
build: {
target: 'esnext',
minify: 'terser',
sourcemap: false,
cssCodeSplit: true
}
});

Or update package.json (if using Create React App):

{
"name": "my-react-app",
"version": "1.0.0",
"private": true,
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}

12. Build Optimization

Create a production build with optimizations:

Terminal window
npm run build

This creates an optimized, minified bundle in the dist/ (Vite) or build/ (Create React App) directory ready for production deployment.

13. Test Production Build Locally

Serve the production build locally:

Terminal window
npm install -g http-server
http-server dist -p 3000

Visit http://localhost:3000 to test your production build.


Local Production Build Test

Before deploying, test your application in a production-like environment:

Terminal window
# Build for production
npm run build
# Serve the production build
http-server dist -p 3000 --gzip

Verify that:

  • All pages load correctly
  • Interactive components work as expected
  • API calls function properly
  • Performance metrics are acceptable
  • No console errors appear in DevTools
  • Mobile responsiveness is correct
  • Forms submit successfully
  • Navigation works smoothly

Deploying with Nixpacks

Nixpacks automatically detects your React application and configures build and runtime environments without requiring a Dockerfile. This is the simplest deployment method for React apps.

Prerequisites for Nixpacks Deployment

  • Your React project pushed to a GitHub repository
  • Valid package.json with build and start scripts
  • No Dockerfile in the repository root (if one exists, Klutch.sh will use Docker instead)

Steps to Deploy with Nixpacks

  1. Push Your React App to GitHub

    Initialize and push your project to GitHub:

    Terminal window
    git init
    git add .
    git commit -m "Initial React app"
    git branch -M main
    git remote add origin git@github.com:YOUR_USERNAME/YOUR_REPO.git
    git push -u origin main
  2. Log In to Klutch.sh Dashboard

    Go to klutch.sh/app and sign in with your GitHub account.

  3. Create a Project

    Navigate to the Projects section and create a new project for your React app.

  4. Create an App

    Click “Create App” and select your GitHub repository.

  5. Select the Branch

    Choose the branch you want to deploy (typically main).

  6. Configure Traffic Type

    Select HTTP as the traffic type for React (a web application serving HTML/CSS/JS).

  7. Set the Internal Port

    Set the internal port to 3000 – this is the port where Nixpacks will serve your React app using a production HTTP server.

  8. Add Environment Variables (Optional)

    Add any environment variables your React app requires:

    REACT_APP_API_URL=https://api.example.com
    REACT_APP_APP_NAME=My React App
    REACT_APP_ANALYTICS_ID=your-analytics-id
    NODE_ENV=production

    If you need to customize the Nixpacks build or start command, use these environment variables:

    • BUILD_COMMAND: Override the default build command (e.g., npm run build)
    • START_COMMAND: Override the default start command (e.g., http-server dist -p 3000)
  9. Configure Compute Resources

    Select your region, compute size, and number of instances based on expected traffic.

  10. Deploy

    Click “Create” to start the deployment. Nixpacks will automatically build and deploy your React app. Your app will be available at a URL like https://example-app.klutch.sh.


Deploying with Docker

For more control over your deployment environment, you can use a Dockerfile. Klutch.sh automatically detects a Dockerfile in your repository root and uses it for deployment.

Creating a Dockerfile for React

Create a Dockerfile in the root of your React project:

# === Build stage ===
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# === Production stage ===
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
# Configure Nginx for React SPA
RUN echo 'server { \
listen 80; \
server_name _; \
root /usr/share/nginx/html; \
index index.html index.htm; \
\
location / { \
try_files $uri $uri/ /index.html; \
} \
\
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { \
expires 1y; \
add_header Cache-Control "public, immutable"; \
} \
}' > /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Alternative Dockerfile Using Node.js http-server

For a lightweight alternative using Node.js http-server:

FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
ENV PORT=3000
EXPOSE 3000
CMD ["npx", "http-server", "dist", "-p", "3000", "--gzip"]

Dockerfile Notes

  • Build stage: Installs dependencies and builds your React app with optimizations.
  • Production stage: Uses Nginx (recommended for SPAs) or http-server to serve your static files.
  • Port: The PORT environment variable is set to 3000 for http-server or 80 for Nginx.
  • Multi-stage build: Reduces final image size by excluding Node.js and build tools from the runtime container.
  • SPA routing: The Nginx configuration includes try_files $uri $uri/ /index.html for proper client-side routing.
  • Caching headers: Nginx configuration includes long-term caching for static assets.

Steps to Deploy with Docker

  1. Create a Dockerfile

    Add the Dockerfile (shown above) to the root of your React repository.

  2. Test Locally (Optional)

    Build and test the Docker image locally:

    Terminal window
    docker build -t react-app:latest .
    docker run -p 3000:80 react-app:latest

    Visit http://localhost:3000 to verify.

  3. Push to GitHub

    Commit and push the Dockerfile and your code:

    Terminal window
    git add Dockerfile
    git commit -m "Add Dockerfile for production deployment"
    git push origin main
  4. Create an App in Klutch.sh

    Go to klutch.sh/app, navigate to “Create App”, and select your repository.

  5. Configure the App
    • Traffic Type: Select HTTP
    • Internal Port: Set to 80 (Nginx) or 3000 (http-server)
    • Environment Variables: Add any required runtime variables
  6. Deploy

    Klutch.sh automatically detects the Dockerfile and uses it to build and deploy your app. Your app will be available at https://example-app.klutch.sh.


Environment Variables

Define environment variables in the Klutch.sh dashboard for production configuration:

REACT_APP_API_URL=https://api.example.com
REACT_APP_APP_NAME=My React App
REACT_APP_ANALYTICS_ID=your-analytics-id
NODE_ENV=production

Accessing Environment Variables

Access environment variables in your React app through process.env:

// In your components or services
const apiUrl = process.env.REACT_APP_API_URL || 'http://localhost:3000';
const appName = process.env.REACT_APP_APP_NAME || 'My App';
const analyticsId = process.env.REACT_APP_ANALYTICS_ID || '';
// Usage example
export function App() {
return (
<div>
<h1>{appName}</h1>
<p>API: {apiUrl}</p>
</div>
);
}

Building with Custom Environment Variables

Ensure environment variables are prefixed with REACT_APP_ to be available in the browser:

Terminal window
REACT_APP_API_URL=https://api.example.com npm run build

Persistent Storage

If your React app generates files, caches data, or needs to store user-generated content on the server, you can use persistent volumes in Klutch.sh.

Adding Persistent Volumes

  1. In the Klutch.sh dashboard, go to your app’s Volumes section.
  2. Click Add Volume.
  3. Set the mount path (e.g., /data, /uploads, /cache).
  4. Set the size (e.g., 1 GiB, 5 GiB).
  5. Save and redeploy your app.

Example: Using localStorage with Server Sync

// Sync browser localStorage with server
function syncDataToServer() {
const userData = localStorage.getItem('userData');
if (userData) {
api.post('/api/user-data', JSON.parse(userData))
.then(() => console.log('Data synced'))
.catch(err => console.error('Sync failed:', err));
}
}
import { useEffect } from 'react';
export function App() {
useEffect(() => {
window.addEventListener('beforeunload', syncDataToServer);
return () => window.removeEventListener('beforeunload', syncDataToServer);
}, []);
// Rest of component
}

Security Best Practices

1. HTTPS/SSL Enforcement

Klutch.sh automatically provides HTTPS for all deployed apps. All traffic is encrypted and secure by default.

2. Content Security Policy

Implement CSP headers to protect against XSS attacks. Configure in Nginx:

add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:;" always;

3. Protect Against CSRF

Use CSRF tokens for forms and API requests:

// Get CSRF token from meta tag or local storage
const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content
|| localStorage.getItem('csrf-token');
// Add to API requests
export const api = {
post: async (endpoint, data) => {
const response = await fetch(`${API_BASE_URL}${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-Token': csrfToken || ''
},
body: JSON.stringify(data)
});
return response.json();
}
};

4. Input Validation and Sanitization

Always validate and sanitize user input:

// Validate email
function validateEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
// Sanitize HTML input
function sanitizeInput(input) {
const div = document.createElement('div');
div.textContent = input;
return div.innerHTML;
}
// Usage in component
export function ContactForm() {
const [email, setEmail] = React.useState('');
const [error, setError] = React.useState('');
const handleSubmit = (e) => {
e.preventDefault();
if (!validateEmail(email)) {
setError('Invalid email address');
return;
}
// Submit form
};
return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
{error && <p style={{ color: 'red' }}>{error}</p>}
<button type="submit">Submit</button>
</form>
);
}

5. Secure API Communication

Always use HTTPS for API calls and include authentication tokens:

export const api = {
baseURL: 'https://api.example.com', // Always HTTPS in production
request: async (method, endpoint, data) => {
const token = localStorage.getItem('authToken');
const response = await fetch(`${api.baseURL}${endpoint}`, {
method,
headers: {
'Content-Type': 'application/json',
'Authorization': token ? `Bearer ${token}` : ''
},
body: data ? JSON.stringify(data) : null
});
if (response.status === 401) {
// Handle unauthorized - redirect to login
window.location.href = '/login';
}
return response.json();
}
};

6. Environment Variable Security

Never commit sensitive data to git. Use environment variables:

// ✗ WRONG - Don't hardcode secrets
const apiKey = 'sk_live_abc123...';
// ✓ CORRECT - Use environment variables
const apiKey = process.env.REACT_APP_API_KEY;
// Never expose secrets in client-side code
// For sensitive operations, call a backend API instead

7. Dependency Security

Keep dependencies updated and audit for vulnerabilities:

Terminal window
npm outdated
npm audit
npm audit fix
npm update

Monitoring and Logging

Performance Monitoring

Monitor React app performance using Web Vitals:

import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function sendToAnalytics(metric) {
if (process.env.NODE_ENV === 'production') {
fetch('/api/metrics', {
method: 'POST',
body: JSON.stringify({
name: metric.name,
value: metric.value
})
});
}
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);

Error Tracking

Implement global error handling:

export function setupErrorTracking() {
window.addEventListener('error', (event) => {
console.error('Global error:', event.error);
if (process.env.NODE_ENV === 'production') {
api.post('/api/errors', {
message: event.message,
stack: event.error?.stack,
url: window.location.href,
timestamp: new Date().toISOString()
});
}
});
window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled promise rejection:', event.reason);
if (process.env.NODE_ENV === 'production') {
api.post('/api/errors', {
message: event.reason?.message,
stack: event.reason?.stack,
type: 'unhandledRejection',
url: window.location.href,
timestamp: new Date().toISOString()
});
}
});
}

Analytics Integration

Add analytics tracking to monitor user behavior:

export function useAnalytics() {
React.useEffect(() => {
// Google Analytics
window.gtag?.('config', 'GA_ID', {
page_path: window.location.pathname,
page_title: document.title
});
// Track custom events
const trackEvent = (eventName, eventData) => {
window.gtag?.('event', eventName, eventData);
};
window.trackEvent = trackEvent;
}, []);
}

Custom Domains

To use a custom domain with your Klutch.sh-deployed React app:

1. Add the Domain in Klutch.sh

In the Klutch.sh dashboard, go to your app’s settings and add your custom domain (e.g., app.example.com).

2. Update Your DNS Provider

Update your DNS records with the CNAME provided by Klutch.sh:

CNAME: app.example.com → example-app.klutch.sh

3. Update Your React App

Update API endpoints if needed:

// API configuration
export const API_BASE_URL = process.env.REACT_APP_API_URL
|| (window.location.hostname.includes('localhost')
? 'http://localhost:3000'
: 'https://api.example.com');

4. Wait for DNS Propagation

DNS changes can take up to 48 hours to propagate. Verify with:

Terminal window
nslookup app.example.com
# or
dig app.example.com CNAME

Once propagated, your React app will be accessible at your custom domain with automatic HTTPS.


Troubleshooting

Issue 1: Build Fails with “Module not found”

Error: Cannot find module 'react' or similar

Solutions:

  • Run npm install to ensure all dependencies are installed
  • Check that package.json includes react and react-dom
  • Delete node_modules and package-lock.json, then run npm install again
  • Verify file paths are correct (case-sensitive on Linux)

Issue 2: Routing Not Working

Error: 404 on page refresh or bookmarked URLs

Solutions:

  • Ensure Nginx configuration includes try_files $uri $uri/ /index.html; for SPA routing
  • Check that your router is configured correctly (react-router or similar)
  • Verify the internal port is set correctly in Klutch.sh dashboard
  • Test locally with production build: npm run build && http-server dist

Issue 3: Environment Variables Not Available

Error: process.env.REACT_APP_API_URL is undefined

Solutions:

  • Ensure environment variables are prefixed with REACT_APP_ for browser access
  • Rebuild after adding new environment variables
  • Check that variables are set in Klutch.sh dashboard (refresh if needed)
  • Test locally with .env file: REACT_APP_API_URL=http://localhost:3000 npm run dev
  • For Node.js-only variables, use process.env (these won’t work in browser)

Issue 4: API Requests Failing (CORS)

Error: CORS error or 401 Unauthorized

Solutions:

  • Ensure API URL is correct (check environment variables)
  • Verify CORS headers on backend API server
  • Check that authentication token is being sent:
    headers: {
    'Authorization': `Bearer ${localStorage.getItem('authToken')}`
    }
  • Use relative URLs if calling same-origin API: /api/users instead of full URL

Issue 5: Performance Issues

Error: Slow initial load or janky animations

Solutions:

  • Check bundle size with Webpack Bundle Analyzer
  • Enable code splitting for routes:
    const Dashboard = React.lazy(() => import('./pages/Dashboard'));
  • Optimize images and assets
  • Use React DevTools Profiler to identify slow components
  • Consider memoization for expensive components:
    export const MemoizedComponent = React.memo(({ data }) => {
    return <div>{data.name}</div>;
    });

Issue 6: Blank Page on Production

Error: App shows blank page or white screen

Solutions:

  • Check browser console for JavaScript errors
  • Verify index.html is being served correctly
  • Check that Nginx/http-server is configured correctly
  • Ensure build completed successfully: check for /dist or /build folder
  • Test production build locally before deploying
  • Clear browser cache (Ctrl+Shift+Delete)

Issue 7: Hot Module Replacement (HMR) Not Working

Error: Changes don’t reflect during development

Solutions:

  • HMR only works in development mode (npm run dev or npm start)
  • Ensure development server is running on the correct port
  • Check firewall settings if developing remotely
  • Restart dev server if issues persist
  • Check vite.config.js or webpack config for HMR settings

Best Practices

1. Component Organization

Organize components by feature or page:

src/components/
├── common/
│ ├── Header.jsx
│ ├── Footer.jsx
│ └── Navigation.jsx
├── dashboard/
│ ├── DashboardLayout.jsx
│ ├── StatsCard.jsx
│ └── Chart.jsx
└── auth/
├── LoginForm.jsx
└── RegisterForm.jsx

2. Use Custom Hooks

Extract component logic into custom hooks:

hooks/useFetch.js
export function useFetch(url) {
const [data, setData] = React.useState(null);
const [loading, setLoading] = React.useState(true);
const [error, setError] = React.useState(null);
React.useEffect(() => {
fetch(url)
.then(r => r.json())
.then(data => { setData(data); setLoading(false); })
.catch(err => { setError(err); setLoading(false); });
}, [url]);
return { data, loading, error };
}

3. Lazy Load Routes

Split code by route for faster initial loads:

const Home = React.lazy(() => import('./pages/Home'));
const Dashboard = React.lazy(() => import('./pages/Dashboard'));
const Settings = React.lazy(() => import('./pages/Settings'));
export function App() {
return (
<Suspense fallback={<Loading />}>
<Router>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Router>
</Suspense>
);
}

4. Use React.memo for Performance

Prevent unnecessary re-renders of expensive components:

const UserCard = React.memo(({ user }) => {
return <div>{user.name}</div>;
});

5. Handle Loading and Error States

Always provide feedback during data loading:

export function UserList() {
const { data, loading, error } = useApi('/api/users');
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
if (!data) return <div>No data</div>;
return (
<ul>
{data.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
);
}

6. Use Semantic HTML

Write accessible HTML for better SEO and UX:

// ✓ Good - semantic HTML
export function Card({ title, content }) {
return (
<article>
<h2>{title}</h2>
<p>{content}</p>
</article>
);
}
// ✗ Avoid - non-semantic divs
export function Card({ title, content }) {
return (
<div>
<div>{title}</div>
<div>{content}</div>
</div>
);
}

7. Test Your Components

Write unit tests for critical components:

import { render, screen } from '@testing-library/react';
import { Counter } from './Counter';
test('Counter increments', () => {
render(<Counter />);
const button = screen.getByRole('button', { name: /increment/i });
button.click();
// Assert counter incremented
});

8. Document Your Components

Add JSDoc comments for clarity:

/**
* Displays a list of users
* @param {Array} users - Array of user objects
* @param {Function} onSelect - Callback when user is selected
* @returns {React.ReactNode} The rendered component
*/
export function UserList({ users, onSelect }) {
return (
<ul>
{users.map(user => (
<li key={user.id} onClick={() => onSelect(user)}>
{user.name}
</li>
))}
</ul>
);
}

9. Monitor Bundle Size

Keep your bundle size optimized:

Terminal window
npm install --save-dev webpack-bundle-analyzer

10. Keep Dependencies Updated

Regularly update React and dependencies:

Terminal window
npm outdated
npm update
npm audit fix

Verifying Your Deployment

After deployment completes:

  1. Check the App URL: Visit your app at https://example-app.klutch.sh or your custom domain.
  2. Test Interactivity: Click buttons, navigate routes, and submit forms.
  3. Check Console: Open F12 and verify no errors appear.
  4. Test API Integration: Verify API calls work and return expected data.
  5. Check Performance: Use Google PageSpeed Insights to verify performance metrics.
  6. Test Responsiveness: Verify mobile, tablet, and desktop layouts work.
  7. Monitor Logs: Check the Klutch.sh dashboard logs for any issues.

If your app doesn’t work as expected, review the troubleshooting section and check the Klutch.sh dashboard logs for detailed error messages.


External Resources


Deploying a React app to Klutch.sh is straightforward with Nixpacks for automatic deployment or Docker for custom environments. By following this guide, you’ve learned how to create a React project with modern tools, build performant components, manage state effectively, integrate with external APIs, optimize builds, configure environment variables, implement security best practices, set up monitoring, and troubleshoot common issues. Your React application is now running on Klutch.sh’s global infrastructure with automatic HTTPS, optimized performance, and reliable hosting. For additional help or questions, consult the official React documentation or contact Klutch.sh support.