React Frontend¶
A React single-page application built with Vite, compiled to static files, and served by Nginx. This example demonstrates the SPA deployment pattern with AppMecca's runtime config injection.
Overview¶
| Repository | git@gitlab.com:appmecca-sample-apps/react-frontend.git |
| Tech Stack | React / Vite / Nginx |
| Services | web (port 80) |
| Database | None |
| Cache | None |
| URL Pattern | https://react-frontend-web.{env}.appmecca.net |
What This App Demonstrates¶
- SPA deployment with
type: spaconfiguration - Runtime config injection via
__appmecca_config.js - Multi-stage Docker builds (Node.js build, Nginx serve)
- The
spa_rootdirective for config file placement - React deployment pattern on AppMecca
appmecca.yaml¶
containers:
- name: web
dockerfile: Dockerfile
context: .
deploy:
- name: web
container: web
type: spa
command: 'nginx -g "daemon off;"' # serves the built bundle from a container
spa_root: /usr/share/nginx/html
port: 80
health_check:
path: /health
Three fields set this apart from a standard static deployment:
type: spa-- tells AppMecca this is a single-page application that needs runtime configurationspa_root-- the directory inside the container where static files are served fromcommand:-- what to run in the container
type: spa must not be ambiguous
A service with type: spa and neither a command: nor a
static_site: block is a hard validation error — the platform cannot
tell whether you want a container or a static deploy, so it refuses the
config rather than guessing. Pick one:
- keep
command:(as above) to run it as a container; or - add a
static_site:block to build the bundle and publish it to object storage behind the edge cache, with no container at all.
Step-by-Step Deployment¶
1. Register the app¶
2. Build the container¶
The build runs a multi-stage Dockerfile: Node.js compiles the React app into static files, then Nginx serves them.
Expected output
3. Deploy¶
Expected output
4. Verify¶
Open the app in your browser:
Key Concepts¶
SPA runtime config injection¶
When you set type: spa, AppMecca generates a __appmecca_config.js file at deploy time and mounts it into the container at {spa_root}/__appmecca_config.js. This file exposes environment variables to your frontend JavaScript at runtime -- without rebuilding the container.
Only prefixed variables are exposed
The generated file contains only variables whose names begin with
PUBLIC_, VITE_, NEXT_PUBLIC_ or REACT_APP_. Everything else is
filtered out so that injected secrets — database passwords, tokens, the
mailer credential — can never reach the browser.
A variable named plain API_URL will not appear in the config file.
Name it PUBLIC_API_URL (or VITE_API_URL) instead.
The generated file looks like this:
// Generated by APPMECCA at deploy time — do not edit
window.__APPMECCA_CONFIG__ = {
"PUBLIC_API_URL": "https://myapp-backend.dev.appmecca.net"
};
To use it, add a script tag to your index.html -- before your bundle:
Then read values in your React code:
const config = window.__APPMECCA_CONFIG__ || {};
const apiUrl = config.PUBLIC_API_URL || "http://localhost:8000";
Why runtime config?
SPAs are compiled to static JavaScript at build time. Without runtime config injection, you would need to rebuild the container every time an environment variable changes. The __appmecca_config.js approach lets you deploy the same image to different environments with different configuration.
How spa_root works¶
The spa_root field tells AppMecca where your static files live inside the container. AppMecca mounts the generated __appmecca_config.js file into this directory. For Nginx-based containers, this is typically /usr/share/nginx/html.
| Web Server | Typical spa_root |
|---|---|
| Nginx | /usr/share/nginx/html |
| Apache | /var/www/html |
| Custom | Wherever your server serves static files from |
Multi-stage Dockerfile¶
The React app uses a two-stage build:
# Stage 1: Build the React app
FROM node:20-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 2: Serve with Nginx
FROM nginx:alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY --from=builder /app/dist/ /usr/share/nginx/html/
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
The final image is around 40 MB -- it contains only Nginx and the compiled static files. No Node.js runtime, no node_modules, no source code.
Connecting to a backend API¶
In a multi-container deployment, use the ${service:backend} template to inject the backend URL as an environment variable:
containers:
- name: web
dockerfile: Dockerfile
context: .
- name: backend
dockerfile: backend/Dockerfile
context: backend/
deploy:
- name: backend
container: backend
port: 8000
path: /api
health_check:
path: /health
- name: web
container: web
type: spa
command: 'nginx -g "daemon off;"' # serves the built bundle from a container
spa_root: /usr/share/nginx/html
port: 80
health_check:
path: /health
environment:
PUBLIC_API_URL: "${service:backend}"
AppMecca resolves ${service:backend} to that service's public HTTPS URL and, because the name carries the PUBLIC_ prefix, writes it into the runtime config file at deploy time. See Inter-Service Communication for details.
The referenced service must exist
${service:backend} is checked at validation time, not at deploy time. If
no service named backend is declared, the config is rejected up front with
a list of the names it does know about — you will not discover the typo from
a broken app later.
Memory floor for SPA services
A type: spa service is validated against a 32 MB minimum for
resources.memory. Below that the nginx worker is killed silently while
the master survives, so the service returns 503 with nothing obvious in
the logs. Leaving resources out entirely is fine — the default is well
above the floor.
SPA vs. SSR
The type: spa setting is for client-side rendered applications (React, Vue, Angular) served as static files. If your frontend uses server-side rendering (Next.js, Nuxt, SvelteKit with adapter-node), do not use type: spa -- deploy it as a regular service instead.
What's Next?¶
- Svelte App -- deploy a Svelte application
- Static Site -- the simpler pattern without SPA config
- Multi-Container App -- pair this frontend with a backend API
- Inter-Service Communication -- connect services with URL templates