Using grabr with Vite + React
Because grabr performs aggressive parallel chunking and raw filesystem writes, it requires a Node.js or Bun backend environment. It cannot run directly inside a Vite React SPA.
In this tutorial, we will build a full-stack application. We will create an Express.js API backend to run grabr, and a Vite React frontend that tells the backend what files to download.
1. Project Setup
mkdir my-grabr-app
cd my-grabr-app
npm create vite@latest frontend -- --template react-ts
cd frontend
npm install
cd ..
mkdir backend
cd backend
npm init -y
npm install express cors @linuxctrl/grabr
npm install -D typescript @types/express @types/cors ts-node2. Build the Express Backend
Create backend/server.ts:
import express from "express";
import cors from "cors";
import path from "path";
import fs from "fs";
import { Downloader } from "@linuxctrl/grabr";
const app = express();
app.use(cors());
app.use(express.json());
const downloader = new Downloader();
downloader.start();
app.post("/api/download", async (req, res) => {
const { url, filename } = req.body;
const outputDir = path.join(process.cwd(), "downloads");
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
try {
const job = await downloader.addJob(url, {
outputDir,
filename,
chunks: 8,
});
console.log(`Job added: ${job.filename}`);
res.json({ success: true, jobId: job.id });
} catch (error) {
console.error("Error starting grabr:", error);
res.status(500).json({ success: false, error: String(error) });
}
});
const PORT = 3001;
app.listen(PORT, () => {
console.log(`Backend running on http://localhost:${PORT}`);
});3. Build the Vite React Frontend
Open frontend/src/App.tsx and replace it with:
import { useState } from 'react'
function App() {
const [url, setUrl] = useState("https://speed.hetzner.de/100MB.bin");
const [status, setStatus] = useState("");
const handleDownload = async () => {
setStatus("Connecting to backend...");
try {
const response = await fetch("http://localhost:3001/api/download", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ url, filename: "test-file.bin" })
});
const data = await response.json();
if (data.success) {
setStatus(`Downloading! Check the backend terminal and downloads folder.`);
} else {
setStatus(`Error: ${data.error}`);
}
} catch (err) {
setStatus("Failed to connect to backend");
}
};
return (
<main style={{ padding: "40px", fontFamily: "sans-serif" }}>
<h1>Grabr Vite Demo</h1>
<div style={{ display: "flex", gap: "10px", marginTop: "20px" }}>
<input
type="text"
value={url}
onChange={(e) => setUrl(e.target.value)}
style={{ width: "300px", padding: "8px" }}
/>
<button onClick={handleDownload} style={{ padding: "8px 16px", cursor: "pointer" }}>
Download via Backend
</button>
</div>
{status && <p style={{ marginTop: "20px", fontWeight: "bold" }}>{status}</p>}
</main>
)
}
export default App4. Run the Full Stack
You need to start both servers. Open two terminal windows.
Terminal 1 (Backend):
cd backend
npx ts-node server.tsTerminal 2 (Frontend):
cd frontend
npm run devOpen the Vite frontend URL in your browser, click Download via Backend, and watch your Express terminal!
Advanced: Live Progress via WebSocket
Want to show real-time progress bars in React? Use a WebSocket library like socket.io to listen to grabr's job:progress events in Express, and emit them to your Vite frontend!