Development and databases

Host React, Vue and static websites connected to external APIs

Reviewed on 14 min read react vue javascript api

Yes, you can host a project built with React, Vue or another JavaScript framework when the build output is a static website. The hosting serves the finished HTML, CSS, JavaScript and image files; it does not need to keep Node.js running to display them.

A modern application is usually split into two parts:

  • Frontend: built with Vite, Webpack, Rollup or another tool, producing a directory such as dist, build or out. You can build it on your account with Node.js and npm, even inside Deploy+, and publish the result as usual.
  • External backend: the database, authentication or storage stays with Supabase, Firebase, Strapi Cloud, Sanity, Airtable or any other provider. The visitor's browser connects to its API over HTTPS, so that service is not installed or run on the hosting.

The hosting only needs to support what runs on it. An external API needs no special support from the hosting.

Two ways to call the external API

In a fully static frontend, the communication follows this path:

Visitante → HTML, CSS y JS del hosting → API del proveedor

Here the browser calls the provider directly. Its API must accept requests from your domain through CORS, and you must configure the allowed domains and the authentication return URLs there.

You can also put a PHP backend hosted on the account in between:

Visitante → frontend del hosting → PHP del hosting → API del proveedor

PHP can make outbound HTTPS requests to external APIs, using cURL, Guzzle or the HTTP client of frameworks such as Laravel. The API must be reachable and allow the network source, the credentials and the operation; its restrictions or the server's may limit a particular connection. This architecture is useful when you need to keep a private key, validate or transform data before sending it, or prevent a sensitive operation from running in the browser. PHP does take part in these requests, but the API and its data are still hosted by the external provider.

The browser's CORS restrictions do not apply to communication between PHP and the provider. The API will still require its usual credentials, permissions and usage limits.

Which projects fit and which need another environment

Project or feature Shared hosting Reason
React, Vue, Svelte or Angular built as a static website Yes The server only delivers the generated files
Vite, Webpack or Rollup project whose output is HTML, CSS and JS Yes Node.js is used during the build, not when visits arrive
Astro or another generator configured for static output Yes All pages are generated before deployment
Next.js with output: 'export' Yes next build creates a static export in out
Frontend that queries Supabase, Firebase, Strapi Cloud, Sanity, Airtable or another API Yes The service runs outside the hosting
Frontend that queries an external API through PHP Yes PHP can make outbound HTTPS requests
Express, Nest, Koa or another Node.js server listening on a port No We do not support programs that listen on a port
Next.js with SSR, Server Actions, dynamic API routes or server functions No It needs the Next.js runtime handling requests
WebSockets server No We do not support programs that listen on a port
Permanent Node.js task No It needs a continuous Node process, which is not officially supported

For cases that need a server or a permanent Node process, use an environment that supports and manages that runtime, such as an application platform or a VPS. Queue workers and consumers written in PHP can stay running as persistent processes on the Avanza, Max and Elástico plans, and real-time features in a Laravel application can be handled with a Pusher-compatible service. You do not always need root access: it depends on the service you choose. The framework's name alone does not decide compatibility: what matters is whether the build ends in static files or whether the application needs to run a server.

The case of Next.js

Next.js can work either way, which is why it often raises questions. To produce a static website, configure the export, for example in next.config.mjs:

const nextConfig = {
  output: 'export',
};

export default nextConfig;

When you run next build, Next.js creates the out directory, which contains the files you need to publish. The Next.js static exports documentation details which features this mode supports.

A static export cannot run, on each visit, logic that depends on a Next.js server, such as SSR, cookies read on the server, Server Actions or dynamic API routes. If the project uses those features, you need to adapt them to an external API or PHP endpoints, or host the application in an environment that supports its server runtime.

Publish the project with Deploy+

You can publish a built frontend with Deploy+: each deployment clones the repository into a new release, runs the build there and only makes it public once every command has finished successfully.

When you connect the repository:

  1. In the Project step, choose Built frontend. Deploy+ suggests dist as the web root and a script that runs npm ci and npm run build.
  2. Check the directory your project generates. Keep dist for the usual Vite setup, but change it to build, out or another path if your tool uses a different name. A Next.js static export uses out.
  3. Review the suggested script. Each line runs in a separate shell, so every line that uses npm, npx or node must activate NVM on that same line. The preset already comes set up this way.
  4. In Automation, turn on Deploy on push if you want every change to the chosen branch to create a new release.

Deploy+ checks for Node.js before running a script that needs it and installs it automatically if it is not yet available on the account. If your package.json requires a specific version, select it before the first deployment from Hosting → your service → Package manager, as explained in the guide to installing Node.js and npm.

If npm ci reports that the lock file is missing or does not match, prepare it in development, test the dependencies and commit package-lock.json next to package.json before deploying. Keeping the lock file means the same dependency versions are installed on every build.

The script runs before the release is activated. If installing dependencies or the build fails, the previous release stays published and visitors never get a half-built website. If you spot a problem after publishing, open the history and use Go back to this version on an earlier release. While that release is still stored, Deploy+ only switches the symbolic link and the rollback takes seconds; if it was already deleted when old releases were cleaned up, it will have to rebuild it from the repository, which takes longer.

Rolling back changes the published code and assets, but it does not undo operations already carried out on the external API or restore the provider's data or configuration.

The Deploy+ .env and the external service's keys

Deploy+ keeps the .env file in the shared folder and links it into each release before running the script. In a static frontend, the bundler reads the variables during the build: there is no Node.js process that reads .env again when a visit arrives.

Any value included in the built JavaScript can be seen by a visitor in their browser. Renaming the variable or keeping it in a .env file does not make it secret.

Only include public values in the bundle, for example:

  • The API URL or the project identifier.
  • A key the provider explicitly describes as public, publishable or intended for the browser.
  • Public analytics or configuration identifiers.

Do not include passwords, admin keys, secret keys, service_role keys, private tokens or database credentials. If an operation needs one of those secrets, do it from PHP and keep the credential in the shared .env or another configuration file outside the web root. Load it in PHP through the framework's configuration system or a .env library, without sending it to the browser or writing it to logs.

Vite includes variables prefixed with VITE_ in the bundle, as its documentation on environment variables warns. Next.js does the same with NEXT_PUBLIC_. These values are also fixed at build time: if you change one, you need to run the build again and deploy the result.

Not all providers name their credentials the same way or offer a key suitable for the browser. Always check their documentation. For example:

  • Supabase intends the publishable key for the browser and reserves the secret key for the server. The old names anon and service_role are their legacy equivalents. Its API keys documentation states that the public key must be paired with Row Level Security and minimal permissions.
  • Firebase's web configuration and keys are public by design. Firebase protects data through Security Rules and App Check, not by hiding that configuration.

Public keys are no substitute for access controls. Turn on, at the provider, the per-user rules, row policies, storage permissions, usage restrictions and any other protections that apply.

Internal routes in an SPA

If your application is an SPA with a single index.html that handles routes in the browser, reloading /panel directly could return a 404 error. In that case, add an .htaccess file to its public directory that keeps real files and sends every other route to index.html:

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ index.html [L]

Put the .htaccess in the directory the tool copies into the build; leaving it in a repository folder that is not published is not enough. The rule should only apply to the frontend: if you share the root with PHP endpoints or API routes, exclude them explicitly so they do not get HTML back.

This rule is for SPAs with client-side routing. Do not use it as is on an export that already generates a separate HTML file for each route.

After publishing, open the website on its final domain and test direct access to an internal route, signing in, the API calls and file uploads if there are any.

CORS does not authenticate users or, on its own, protect the API from other clients. If you add a PHP intermediary, validate the session and permissions and limit destinations and operations: do not create an open proxy that accepts any URL from the visitor. Also set timeouts and check the result before repeating operations that have side effects.

You may also find useful

Something wrong or out of date? Tell us and we will review it.