Deploying a React app to SiteGround takes about ten minutes: run npm run build on your own machine, upload the contents of dist to public_html, and add a three line .htaccess rewrite so client side routes do not return 404. That covers every static React build, which is what most React apps are. Only server rendered apps need SiteGround’s Node.js project feature, and that is not available on the entry level plan.
dist/ to public_html through Site Tools File Manager or FTP, then add an .htaccess that rewrites all non file requests to index.html. For SSR with Next.js or Remix, use Node.js Projects, which requires GrowBig (5 projects), GoGeek (10) or Cloud (unlimited). Never run the build on a shared server.SiteGround is an Apache and PHP host at heart, and a compiled React bundle is just HTML, JavaScript and CSS files. That is why the static route is so simple and so reliable. The complications only appear when you want the server to execute JavaScript, which is a different product tier and a different mental model. This guide covers both, plus the reason building on the server is a bad habit worth breaking.
Build the app locally with Vite
Start from a clean production build. Vite 8 is the current major release and React 19 is the current runtime, and the defaults from a fresh scaffold are sensible for shared hosting without changes.
npm create vite@latest my-app -- --template react
cd my-app
npm install
npm run build
# Sanity check the production bundle before uploading
npm run previewThe output lands in dist/. Open it and you should see index.html, an assets/ folder with hashed filenames, and whatever you put in public/. Those hashed names matter later, because they are what makes aggressive browser caching safe.
If the app will live in a subdirectory rather than at the domain root, set the base path before building or every asset URL will point at the wrong place.
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
base: '/app/',
build: {
outDir: 'dist',
sourcemap: false,
chunkSizeWarningLimit: 900
}
})sourcemap: false for production uploads on shared hosting. Source maps can easily double your upload size and they hand your unminified source to anyone who opens developer tools.Upload the build to public_html
You want the contents of dist in public_html, not the dist folder itself. Getting that wrong is the most common deployment mistake and it produces a directory listing or a 403 rather than an obvious error.
The fastest path is a compressed upload. In Site Tools open Site, then File Manager, navigate to public_html, and use Upload. A React build is many small files and each one costs a round trip over FTP, so a single archive uploads far faster than the loose tree.
# Zip the contents, not the folder
cd dist
zip -r ../build.zip .
cd ..
# Or push straight over SFTP if you prefer the command line
rsync -avz --delete dist/ user@server.siteground.biz:~/www/yourdomain.com/public_html/After extracting, confirm that public_html/index.html exists at the top level and that public_html/assets/ sits beside it. If you see public_html/dist/index.html, move everything up one level.
Add the SPA rewrite rule
Load the site now and the home page works. Click into a route, refresh, and you get a 404. That is Apache doing exactly what it should: there is no file at /dashboard, so it returns not found before React ever loads. The fix is a rewrite that serves index.html for anything that is not a real file or directory.
Create public_html/.htaccess with this content.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]
</IfModule>
<IfModule mod_headers.c>
# Hashed asset filenames are safe to cache hard
<FilesMatch "\.(js|css|woff2|svg|png|jpg|webp)$">
Header set Cache-Control "public, max-age=31536000, immutable"
</FilesMatch>
# The entry document must never be cached
<FilesMatch "index\.html$">
Header set Cache-Control "no-cache, must-revalidate"
</FilesMatch>
</IfModule>The caching half is not optional if you care about repeat visits. Vite fingerprints every asset filename, so those files can be cached for a year without risk, while index.html must revalidate every time or users will keep loading a stale bundle after you deploy.
RewriteBase and the target path must both change, for example RewriteBase /app/ and RewriteRule . /app/index.html [L]. Mismatching those two is the usual cause of an infinite redirect.When you actually need Node.js Projects
Everything above assumes a client rendered app. If you are running Next.js with server components, Remix, or an Express API alongside the front end, you need a Node process, and SiteGround supports that through Node.js Projects. Availability is plan gated, as SiteGround’s knowledge base spells out.
| Plan | Node.js projects | Static React | Verdict |
|---|---|---|---|
| StartUp | Not supported | Yes | Fine for a Vite build, no SSR |
| GrowBig | Up to 5 | Yes | The realistic entry point for SSR |
| GoGeek | Up to 10 | Yes | Multiple apps plus staging |
| Cloud | Unlimited | Yes | Dedicated resources, no project cap |
To create one, go to Client Area, Websites, Node.js Projects, and click New Project. The wizard offers two deployment paths: import a GitHub repository, which is the one to pick because it can redeploy automatically on push, or upload an archive as .zip, .tar.gz or .tgz up to 128 MB. The archive route means manual reuploads for every change, which gets old quickly.
If you are weighing this against other hosts before committing, our overview of installing Node.js on cloud hosting compares the managed and self managed routes side by side.
Why building on the server is usually a mistake
People try it constantly and it fails for consistent reasons. A shared account limits memory per process, caps concurrent processes, and counts inodes. npm install on a modern React project creates tens of thousands of files in node_modules, and the bundler wants hundreds of megabytes of RAM at peak. On a plan sized for PHP, that combination gets killed partway through.
| Build locally, upload dist | Build on the shared server |
|---|---|
| Uploads a few hundred KB of hashed assets | Writes tens of thousands of node_modules files against your inode quota |
| Peak memory used on your laptop | Bundler competes with PHP for a capped memory pool |
| Deterministic output you already tested | Node version on the server may not match yours |
| A failed build never touches the live site | A failed build can leave the site half updated |
The right compromise is a CI step. Let GitHub Actions run npm ci && npm run build and push only dist/ to SiteGround over SFTP. You get reproducible builds without asking a shared server to do work it was never provisioned for. The same reasoning applies to other front end frameworks, which we cover in the guides on deploying Vue.js and running Gatsby on Vultr.
Troubleshooting
Blank white page, console shows 404s for /assets/index-abc123.js. The base path is wrong. If the app lives at the domain root, base should be '/'. If it lives in a folder, base must match that folder exactly, including the trailing slash. Rebuild after changing it, because the paths are baked into the bundle.
Home page works, refreshing any other route gives 404. The .htaccess is missing, empty, or not being read. Many FTP clients hide dotfiles by default. Confirm it is there and non empty in File Manager, and check that its rewrite target matches your deployment directory.
Changes are live but users still see the old app. index.html is being cached. Add the no cache header shown above, then purge the SiteGround dynamic cache from Site Tools under Speed, Caching. Hard refreshing your own browser proves nothing about what other visitors get.
API calls fail with a CORS error after deploying. In development Vite proxies your API, which hides the cross origin problem. In production the browser talks to the API directly. Either serve the API from the same domain under a path like /api, or set proper CORS headers on the API side.
Node.js project starts then immediately stops. The start command or entry file is wrong, or the app binds a hard coded port. Read the port from process.env.PORT rather than assigning 3000 yourself, and check the project logs in the Node.js Projects panel for the actual exit reason.
Frequently asked questions
Can I host a React app on SiteGround shared hosting?
Yes. A production React build is static HTML, CSS and JavaScript, which any Apache host serves without special support. Upload the build output to public_html and add an .htaccess rewrite for client side routing. Every SiteGround plan handles this, including StartUp.
Does SiteGround support Node.js?
Yes, through Node.js Projects, but not on StartUp. GrowBig allows up to 5 projects, GoGeek up to 10, and Cloud plans are unlimited. You only need this for server rendered frameworks or a Node API. A plain React single page app does not require it.
Should I use Create React App or Vite?
Use Vite. Create React App is no longer the recommended way to start a React project, and Vite produces smaller bundles with a much faster dev server. The deployment steps in this guide are identical either way, only the output directory name differs.
How do I set environment variables for the build?
Vite exposes variables prefixed with VITE_ at build time, read through import.meta.env. They are compiled into the bundle, so treat them as public. Never put an API secret in one. Anything that must stay private belongs on a server you control.
Can I automate deployments to SiteGround?
Yes. For static builds, use a GitHub Actions workflow that builds and pushes over SFTP with your SiteGround credentials stored as repository secrets. For Node.js Projects, connect the GitHub repository in the setup wizard and enable automatic redeployment on push.
The bottom line
Running React on SiteGround is a static hosting problem, not a Node hosting problem, for the large majority of apps. Build with Vite on your own machine, upload the contents of dist into public_html, add the rewrite and caching rules, and you are done. Nothing about that flow depends on your plan tier.
Reach for Node.js Projects only when your framework genuinely needs a running server, and remember it starts at GrowBig. Whatever you do, keep the build off the shared server. The five minutes it saves is never worth the memory limits, the inode consumption and the chance of leaving a half deployed site to your users.
