React SPA + StorageVault (S3 Static Hosting)
In this tutorial, we will build a React SPA application that uses Pilvio StorageVault (S3) for hosting static files and uploading files.
⏳ CDN coming soon: Pilvio CDN is under development. For now, static files can be served directly from StorageVault's S3. Once CDN becomes available, you can easily add caching and geographic distribution.
What we will build
- A React SPA that communicates with a Pilvio backend
- File uploads directly to StorageVault (presigned URLs)
- Static deploy to an S3 bucket
Prerequisites
- Pilvio account, API token, and S3 keys (see overview)
- Node.js 20+ and npm
- Backend API (recommended: FastAPI + StorageVault)
Step 1: Create the React project
npm create vite@latest pilvio-react-app -- --template react
cd pilvio-react-app
npm install
npm install axios
Step 2: Create the Pilvio API client
Create the file src/lib/pilvio-client.js:
import axios from 'axios';
// Backend API URL (mitte otse Pilvio API, vaid sinu backend)
const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8000';
const api = axios.create({
baseURL: API_BASE,
headers: {
'Content-Type': 'application/json',
},
});
// Failide üleslaadimine backend proxy kaudu
export async function uploadFile(file) {
const formData = new FormData();
formData.append('file', file);
const response = await api.post('/api/v1/files/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' },
});
return response.data;
}
// Failide loetelu
export async function listFiles(prefix = 'uploads/') {
const response = await api.get('/api/v1/files', {
params: { prefix },
});
return response.data;
}
// Allalaadimis-URL hankimine (eelsigneeritud S3 URL)
export async function getDownloadUrl(fileKey) {
const response = await api.get(`/api/v1/files/download-url/${fileKey}`);
return response.data;
}
// Faili kustutamine
export async function deleteFile(fileKey) {
const response = await api.delete(`/api/v1/files/${fileKey}`);
return response.data;
}
export default api;
Security: Never put Pilvio API tokens or S3 keys into a React application! All Pilvio API requests must go through your backend server.
Step 3: File management component
Create the file src/components/FileManager.jsx:
import { useState, useEffect } from 'react';
import { uploadFile, listFiles, getDownloadUrl, deleteFile } from '../lib/pilvio-client';
export default function FileManager() {
const [files, setFiles] = useState([]);
const [uploading, setUploading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
loadFiles();
}, []);
async function loadFiles() {
try {
const data = await listFiles();
setFiles(data.files || []);
} catch (err) {
setError('Failide laadimine ebaõnnestus');
}
}
async function handleUpload(event) {
const file = event.target.files[0];
if (!file) return;
setUploading(true);
setError(null);
try {
await uploadFile(file);
await loadFiles();
} catch (err) {
setError('Üleslaadimine ebaõnnestus');
} finally {
setUploading(false);
}
}
async function handleDownload(fileKey) {
try {
const data = await getDownloadUrl(fileKey);
window.open(data.url, '_blank');
} catch (err) {
setError('Allalaadimine ebaõnnestus');
}
}
async function handleDelete(fileKey) {
if (!confirm('Kas oled kindel?')) return;
try {
await deleteFile(fileKey);
await loadFiles();
} catch (err) {
setError('Kustutamine ebaõnnestus');
}
}
function formatSize(bytes) {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1048576) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / 1048576).toFixed(1)} MB`;
}
return (
<div style={{ maxWidth: '800px', margin: '0 auto', padding: '20px' }}>
<h1>StorageVault failihaldus</h1>
{error && (
<div style={{ color: 'red', padding: '10px', marginBottom: '10px' }}>
{error}
</div>
)}
<div style={{ marginBottom: '20px' }}>
<input type="file" onChange={handleUpload} disabled={uploading} />
{uploading && <span> Laadin üles...</span>}
</div>
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr>
<th style={{ textAlign: 'left', padding: '8px' }}>Fail</th>
<th style={{ textAlign: 'right', padding: '8px' }}>Suurus</th>
<th style={{ textAlign: 'right', padding: '8px' }}>Tegevused</th>
</tr>
</thead>
<tbody>
{files.map((file) => (
<tr key={file.key} style={{ borderTop: '1px solid #eee' }}>
<td style={{ padding: '8px' }}>{file.key.split('/').pop()}</td>
<td style={{ textAlign: 'right', padding: '8px' }}>{formatSize(file.size)}</td>
<td style={{ textAlign: 'right', padding: '8px' }}>
<button onClick={() => handleDownload(file.key)}>Lae alla</button>{' '}
<button onClick={() => handleDelete(file.key)}>Kustuta</button>
</td>
</tr>
))}
</tbody>
</table>
{files.length === 0 && <p>Faile ei ole. Laadi midagi üles!</p>}
</div>
);
}
Step 4: Environment variables
Create the file .env:
VITE_API_URL=https://sinu-api-domeen.ee
Step 5: Static deploy to StorageVault (S3)
Build the React application and upload it to an S3 bucket:
# Ehita
npm run build
# Laadi dist/ sisu S3-sse (AWS CLI Pilvio endpointiga)
aws s3 sync dist/ s3://minu-react-app/ \
--endpoint-url https://s3.pilvio.com:8080 \
--delete
# Sea index.html Content-Type
aws s3 cp s3://minu-react-app/index.html s3://minu-react-app/index.html \
--endpoint-url https://s3.pilvio.com:8080 \
--content-type "text/html" \
--metadata-directive REPLACE
Alternative: Deploy script
Create the file deploy.sh:
#!/bin/bash
set -e
echo "Ehitan React rakendust..."
npm run build
echo "Laadin üles StorageVault'i..."
aws s3 sync dist/ s3://minu-react-app/ \
--endpoint-url https://s3.pilvio.com:8080 \
--delete \
--cache-control "public, max-age=31536000" \
--exclude "index.html"
# index.html ilma vahemäluta (et uus versioon oleks kohe näha)
aws s3 cp dist/index.html s3://minu-react-app/index.html \
--endpoint-url https://s3.pilvio.com:8080 \
--content-type "text/html" \
--cache-control "no-cache"
echo "Deploy valmis!"
chmod +x deploy.sh
./deploy.sh
Alternative: SPA hosting on Pilvio VM + Nginx
If setting up S3 static hosting is complex, you can also serve the SPA via Nginx on a VM:
# Kopeeri ehitatud failid serverisse
scp -r dist/* deploy@SINU_FLOATING_IP:/var/www/react-app/
# Nginx seadistus SPA jaoks
sudo tee /etc/nginx/sites-available/react-app <<'EOF'
server {
listen 80;
server_name sinu-domeen.ee;
root /var/www/react-app;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /assets/ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
EOF
⏳ CDN (coming soon): Pilvio CDN will allow you to add automatic caching and edge serving to your S3 bucket in the future. The current S3-based solution works well, and adding CDN will only require changing the URL.
Next steps: Connect with a backend API (Node.js or FastAPI) and add a database.