¡Felicidades! 🎉 Si has llegado hasta aquí, tienes una aplicación React completa funcionando en tu ordenador (localhost). Navega, pide datos, tiene estilos y está optimizada.
Pero hay un problema (pequeñito… na.. poca cosa): solo tú puedes verla 🤔.
El paso final de cualquier desarrollo de software es el Despliegue (Deployment): subir nuestra aplicación a internet para que el resto del mundo pueda usarla.
Pero antes de subir nada, debemos entender que el código que hemos escrito no es el código que el navegador va a ejecutar finalmente. Necesitamos hacer el Build.
Desarrollo vs Producción
Cuando ejecutamos npm run dev, Vite inicia un servidor de desarrollo pensado para nosotros, los programadores:
- Tiene Hot Module Replacement (HMR) para ver cambios instantáneos.
- Los mensajes de error son detallados.
- El código está “suelto” (cientos de archivos) para facilitar la depuración.
Esto es genial para programar, pero no es lo que queremos servir al usuario final. Es más lento, más pesado y está pensado para depurar.
Para el usuario, queremos:
- Un solo archivo JS compactado (Minified).
- Código optimizado (Tree Shaking, Code Splitting).
- Sin herramientas de depuración que pesen.
El comando npm run build
Para generar esta versión optimizada, volvemos a la terminal, paramos el servidor de desarrollo (Ctrl+C) y ejecutamos:
npm run build
Veréis que Vite empieza a trabajar y, en unos segundos, os mostrará un resumen de los archivos generados.
La carpeta dist
Si miráis vuestro proyecto, veréis que ha aparecido una carpeta nueva llamada dist (distribution).
Esta carpeta es vuestra aplicación.
Todo lo que hay en src, node_modules, etc., ya no importa. Vite ha cogido todo vuestro código, lo ha traducido, comprimido y empaquetado dentro de dist.
Dentro veréis generalmente:
index.html: La entrada.assets/: Vuestros JS y CSS con nombres raros (hashes) para evitar problemas de caché (ej:index-XyZ123.js).
No intentéis abrir el index.html con doble clic. Si lo hacéis, veréis la pantalla en blanco. Las aplicaciones modernas necesitan servirse a través de protocolo HTTP, no de protocolo de archivo.
Desplegar en un VPS
Una aplicación React creada con Vite es, al final, una web estática. No necesitamos Node.js ejecutándose en producción para servirla. Necesitamos un servidor web, como Nginx, entregando la carpeta dist.
Vamos a empezar por la opción que más control nos da: un VPS propio.
Un VPS no es más que un servidor (generalmente Linux) alquilado en algún proveedor (Hetzner, OVH, DigitalOcean, Linode, el que queráis).
La diferencia importante es que aquí no le damos nuestra aplicación a una plataforma para que decida por nosotros. Nosotros servimos los ficheros.
Qué necesitamos
Para este despliegue necesitamos:
- Un VPS con Linux (Ubuntu Server, por ejemplo).
- Acceso por SSH.
- Un dominio apuntando al servidor.
- Nginx para servir los archivos.
- Certbot para activar HTTPS.
La idea será esta:
React + Vite
↓ npm run build
dist/
↓ subir al VPS
/var/www/mi-app/
↓ Nginx
https://midominio.com
Preparar el servidor
Entramos por SSH en nuestro VPS:
ssh usuario@IP_DEL_SERVIDOR
Actualizamos paquetes e instalamos Nginx:
sudo apt update
sudo apt install nginx
Creamos una carpeta para nuestra aplicación:
sudo mkdir -p /var/www/mi-app
sudo chown -R $USER:$USER /var/www/mi-app
Estoy usando /var/www/mi-app porque es una ubicación típica para webs en Linux. Podéis usar otra ruta, pero intentad no desperdigar proyectos por carpetas raras. Luego no hay quien se acuerde de nada.
Subir la carpeta dist
Tenemos dos opciones habituales:
- Construir en local y subir la carpeta
dist. - Subir el código al VPS y ejecutar
npm run buildallí.
Para una app React estática, construir en local y subir dist es perfectamente válido. Desde vuestro ordenador, después de ejecutar npm run build, podéis usar rsync:
rsync -avz --delete dist/ usuario@IP_DEL_SERVIDOR:/var/www/mi-app/
El flag --delete hace que el servidor quede igual que vuestra carpeta dist. Si habéis borrado un archivo antiguo, también se borra en el VPS.
Revisad bien la ruta antes de usar --delete. Es muy útil, pero también muy obediente. Si le decís que borre donde no debe, lo hará sin discutir.
Si estáis en Windows y no tenéis rsync, podéis usar scp o cualquier cliente SFTP.
scp -r dist/* usuario@IP_DEL_SERVIDOR:/var/www/mi-app/
En Windows también podráis usar WSL, cosa bastante recomendable
Configurar Nginx
Ahora vamos a decirle a Nginx que sirva nuestra aplicación. Creamos un archivo de configuración:
sudo nano /etc/nginx/sites-available/mi-app
Contenido básico:
server {
listen 80;
server_name midominio.com www.midominio.com;
root /var/www/mi-app;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /assets/ {
try_files $uri =404;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
Activamos el sitio:
sudo ln -s /etc/nginx/sites-available/mi-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
La línea importante es try_files $uri $uri/ /index.html;. Si el navegador pide /contacto y no existe un archivo real llamado contacto, Nginx devuelve index.html. Después, React Router decide qué componente pintar.
Actualizar la aplicación
Cada vez que queramos publicar una nueva versión:
npm run build
rsync -avz --delete dist/ usuario@IP_DEL_SERVIDOR:/var/www/mi-app/
Y ya está. No hay que reiniciar Node, ni levantar procesos, ni mantener PM2. Nginx está sirviendo archivos estáticos.
Si queréis automatizar esto, podéis crear un pequeño script deploy.sh con el build y el rsync. No hace falta montar una plataforma entera para empezar a desplegar con comodidad.
