Skip to main content
RapidDev - Software Development Agency
replit-tutorial

How to store static files in Replit

Replit provides Object Storage backed by Google Cloud Storage for handling static assets like images, videos, and documents. Upload files through the Object Storage pane in your workspace, access them via auto-generated public URLs, and reference those URLs in your code. For deployed apps, remember that the local filesystem resets on every publish, so you must store persistent files in Object Storage rather than the project directory.

What you'll learn

  • Understand why Replit deployments reset the filesystem on every publish
  • Upload and manage files using Replit Object Storage
  • Serve static assets from a public folder during development
  • Reference stored files via CDN-style public URLs in your app code
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner9 min read15 minutesAll Replit plans (Starter, Core, Pro, Enterprise)March 2026RapidDev Engineering Team
TL;DR

Replit provides Object Storage backed by Google Cloud Storage for handling static assets like images, videos, and documents. Upload files through the Object Storage pane in your workspace, access them via auto-generated public URLs, and reference those URLs in your code. For deployed apps, remember that the local filesystem resets on every publish, so you must store persistent files in Object Storage rather than the project directory.

Store and Serve Static Files in Replit with Object Storage

This tutorial explains how to handle static assets like images, PDFs, and media files in Replit projects. You will learn why the local filesystem is not reliable for persistent storage in deployed apps, how to use Replit Object Storage for durable file hosting, and how to serve files through public URLs or a dedicated public folder during development. By the end, you will have a working pattern for uploading and serving assets in both development and production.

Prerequisites

  • A Replit account on any paid plan (Core or Pro recommended for deployments)
  • A working Repl with a web app (Node.js, Python, or any framework)
  • Basic understanding of how web apps serve images and files
  • Familiarity with environment variables and the Replit Secrets tool

Step-by-step guide

1

Understand filesystem non-persistence in deployments

Before storing any files, you need to understand a critical Replit behavior: the filesystem resets every time you publish a deployment. Files you save to disk during development will not exist in production after a redeploy. This catches many beginners off guard. If your app allows users to upload profile pictures and you save them to a local uploads folder, those files vanish on the next deploy. This is by design because Replit deployments run on fresh containers. You must use external storage for any files that need to persist.

Expected result: You understand that only Object Storage and databases persist data across deployments.

2

Open Object Storage in your workspace

In your Replit workspace, click the plus icon on the Tools dock in the left sidebar. Search for Object Storage and click it to open the storage pane. Object Storage is backed by Google Cloud Storage and provides a simple interface for uploading, listing, and deleting files. Each file you upload gets a publicly accessible URL that you can use in your application. The storage pane shows all uploaded files with their names, sizes, and URLs.

Expected result: The Object Storage pane opens showing an empty file list or any previously uploaded files.

3

Upload static assets through the UI

Click the Upload button in the Object Storage pane and select files from your computer. You can upload images, fonts, PDFs, videos, and any other static file. After upload completes, each file gets a public URL that looks like a Google Cloud Storage endpoint. Click the copy icon next to any file to copy its URL. You can organize files by using path-style names like images/logo.png or assets/hero-banner.jpg. These URLs are permanent and do not change between deployments.

Expected result: Your files appear in the Object Storage list with copyable public URLs.

4

Reference stored files in your application code

Use the public URLs from Object Storage directly in your application. In React components, set image src attributes to the storage URL. In CSS, use the URL for background images. For server-side rendering, you can fetch files from the URL using standard HTTP libraries. Store the base URL as an environment variable in Secrets if you want to switch storage providers later without changing code throughout your app.

typescript
1// React component example
2import React from 'react';
3
4const STORAGE_BASE = process.env.REPLIT_OBJECT_STORAGE_URL || '';
5
6export function Logo() {
7 return (
8 <img
9 src={`${STORAGE_BASE}/images/logo.png`}
10 alt="Company Logo"
11 width={200}
12 height={60}
13 />
14 );
15}
16
17export function HeroBanner() {
18 return (
19 <div
20 style={{
21 backgroundImage: `url(${STORAGE_BASE}/images/hero.jpg)`,
22 backgroundSize: 'cover',
23 height: '400px'
24 }}
25 >
26 <h1>Welcome</h1>
27 </div>
28 );
29}

Expected result: Your app displays images and assets loaded from Object Storage URLs.

5

Use a public folder for development-only static files

During development, you can place static files in a public or static folder at the root of your project. Most frameworks like React (via Vite), Next.js, and Express serve files from this folder automatically. This is convenient for assets that ship with your code like favicons, fonts, and placeholder images. Since these files are part of your project, they persist across deployments because they are included in the build output. However, user-uploaded content should never go here because the filesystem resets.

typescript
1// Express.js: serve a public folder
2import express from 'express';
3const app = express();
4
5// Serve files from the public directory
6app.use('/assets', express.static('public'));
7
8// Now files in /public/logo.png are accessible at /assets/logo.png
9app.listen(3000, '0.0.0.0', () => {
10 console.log('Server running on port 3000');
11});

Expected result: Static files in the public folder are accessible via your app's URL during development and after deployment.

6

Handle file uploads programmatically with Object Storage

For apps that accept user uploads, write server-side code that receives the file and stores it in Object Storage using the Replit client library. The @replit/object-storage package provides simple put and get methods. Install it via the Shell with npm install @replit/object-storage. After storing a file, save the returned URL to your database so your app can reference it later. This pattern ensures uploaded files survive deployments and container resets.

typescript
1import { Client } from '@replit/object-storage';
2import express from 'express';
3import multer from 'multer';
4
5const app = express();
6const storage = new Client();
7const upload = multer({ storage: multer.memoryStorage() });
8
9app.post('/upload', upload.single('file'), async (req, res) => {
10 try {
11 const fileName = `uploads/${Date.now()}-${req.file.originalname}`;
12 await storage.uploadFromBytes(fileName, req.file.buffer);
13 const url = await storage.getSignedUrl(fileName);
14 res.json({ success: true, url });
15 } catch (error) {
16 console.error('Upload failed:', error);
17 res.status(500).json({ error: 'Upload failed' });
18 }
19});
20
21app.listen(3000, '0.0.0.0');

Expected result: Uploaded files are stored in Object Storage and the API returns a URL you can use in your frontend.

Complete working example

server.js
1import express from 'express';
2import multer from 'multer';
3import { Client } from '@replit/object-storage';
4import path from 'path';
5
6const app = express();
7const storage = new Client();
8const upload = multer({
9 storage: multer.memoryStorage(),
10 limits: { fileSize: 5 * 1024 * 1024 } // 5 MB limit
11});
12
13// Serve static assets from the public folder
14app.use('/assets', express.static('public'));
15
16// Serve the upload form
17app.get('/', (req, res) => {
18 res.send(`
19 <h1>File Upload Demo</h1>
20 <form action="/upload" method="POST" enctype="multipart/form-data">
21 <input type="file" name="file" accept="image/*,.pdf" />
22 <button type="submit">Upload</button>
23 </form>
24 <div id="files"></div>
25 `);
26});
27
28// Handle file upload to Object Storage
29app.post('/upload', upload.single('file'), async (req, res) => {
30 if (!req.file) {
31 return res.status(400).json({ error: 'No file provided' });
32 }
33
34 try {
35 const ext = path.extname(req.file.originalname);
36 const fileName = `uploads/${Date.now()}${ext}`;
37
38 await storage.uploadFromBytes(fileName, req.file.buffer);
39 const url = await storage.getSignedUrl(fileName);
40
41 res.json({
42 success: true,
43 fileName,
44 url,
45 size: req.file.size
46 });
47 } catch (error) {
48 console.error('Upload error:', error.message);
49 res.status(500).json({ error: 'Failed to store file' });
50 }
51});
52
53// List all stored files
54app.get('/files', async (req, res) => {
55 try {
56 const files = await storage.list();
57 res.json({ files });
58 } catch (error) {
59 console.error('List error:', error.message);
60 res.status(500).json({ error: 'Failed to list files' });
61 }
62});
63
64// Delete a stored file
65app.delete('/files/:name', async (req, res) => {
66 try {
67 await storage.delete(req.params.name);
68 res.json({ success: true });
69 } catch (error) {
70 console.error('Delete error:', error.message);
71 res.status(500).json({ error: 'Failed to delete file' });
72 }
73});
74
75const PORT = 3000;
76app.listen(PORT, '0.0.0.0', () => {
77 console.log(`Server running on port ${PORT}`);
78});

Common mistakes when storing static files in Replit

Why it's a problem: Saving uploaded files to a local folder like /uploads and expecting them to persist in production

How to avoid: Use Replit Object Storage instead. The filesystem resets on every deployment, so local files are lost.

Why it's a problem: Binding the server to localhost or 127.0.0.1 instead of 0.0.0.0

How to avoid: Always use 0.0.0.0 as the host. Replit deployments cannot detect ports bound to localhost, causing the error hostingpid1: an open port was not detected.

Why it's a problem: Hardcoding Object Storage URLs directly in frontend code instead of using environment variables

How to avoid: Store the base storage URL in Secrets (Tools > Secrets) and reference it via process.env. This makes it easy to change providers later.

Why it's a problem: Uploading very large files without setting size limits, causing the Repl to run out of memory

How to avoid: Configure multer or your upload handler with a fileSize limit. On the free tier (2 GiB RAM), keep uploads under 5 MB per file.

Best practices

  • Never store user-uploaded files on the local filesystem in Replit because deployments reset the disk on every publish
  • Use Object Storage for any file that needs to persist across deployments, including images, documents, and media
  • Store the Object Storage base URL in Secrets so you can swap providers without changing application code
  • Set file size limits on upload endpoints to prevent out-of-memory crashes, especially on the free tier with 2 GiB RAM
  • Use path-style naming conventions like uploads/2024/image.png to keep Object Storage organized
  • Bind your server to 0.0.0.0 instead of localhost so Replit deployments can detect the open port
  • Place build-time static assets like favicons and fonts in the public folder since they deploy with your code
  • Monitor storage usage in the Resources panel to stay within your plan allocation

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I am building a web app on Replit that needs to handle user-uploaded images. The filesystem resets on deployment. How do I use Replit Object Storage to persist uploaded files and serve them via public URLs? Give me a complete Express.js example with multer for uploads and the @replit/object-storage client library.

Replit Prompt

Add a file upload feature to my app. Users should be able to upload images up to 5 MB. Store files in Replit Object Storage so they persist across deployments. Show uploaded images in a gallery page. Use Express with multer on the backend and display a simple upload form on the frontend.

Frequently asked questions

No. The filesystem resets every time you publish a deployment. Files written to disk during runtime are lost when the container restarts. Use Object Storage for any files that need to persist.

Object Storage is a file hosting service backed by Google Cloud Storage. It provides durable storage with public URLs for your files. Storage costs depend on your plan and usage. Check the Resources panel in your workspace for current allocation and usage details.

Yes, for assets that ship with your code like favicons, fonts, and icons. These files are included in the build output and deploy with your app. However, do not use the public folder for user-uploaded content because it requires a redeploy to update.

Copy the public URL from the Object Storage pane and use it as the src attribute in an img tag. For dynamic content, store the base URL in Secrets and build full paths in code using process.env or import.meta.env for Vite-based projects.

Yes. You can place a CDN in front of your Object Storage URLs by configuring the CDN to proxy requests to the storage endpoint. This adds caching and reduces latency for global users.

Any file type is supported, including images (PNG, JPG, SVG, WebP), documents (PDF, DOCX), media (MP4, MP3), fonts (WOFF2, TTF), and data files (JSON, CSV). There is no restriction on file format.

You are likely saving files to the local filesystem instead of Object Storage. Replit deployments run on fresh containers that do not retain files from previous runs. Switch to Object Storage to persist uploads permanently.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.