Cómo Configuré Firebase en un Proyecto Astro Sin Perder Horas
La primera vez que agregué Firebase a un proyecto en Astro, seguí la documentación oficial de Firebase al pie de la letra y me encontré con errores de “window is not defined” y variables de entorno que no llegaban al cliente. Aquí está la configuración que finalmente funcionó sin errores.
El problema: Astro renderiza en el servidor por defecto
Firebase Web SDK espera correr en el navegador, pero Astro renderiza los componentes en el servidor durante el build. Si inicializas Firebase directamente en el frontmatter de un archivo .astro, vas a toparte con errores porque window no existe ahí.
Paso 1: Variables de entorno con el prefijo correcto
Astro solo expone al cliente las variables de entorno que empiezan con PUBLIC_:
PUBLIC_FIREBASE_API_KEY=tu-api-key
PUBLIC_FIREBASE_AUTH_DOMAIN=tu-proyecto.firebaseapp.com
PUBLIC_FIREBASE_PROJECT_ID=tu-proyecto
Si usas variables sin ese prefijo, van a estar disponibles en el servidor, pero undefined en cualquier código que corra en el navegador.
Paso 2: Aislar la inicialización de Firebase en su propio módulo
En vez de inicializar Firebase directamente en un componente, lo centralicé en un archivo aparte:
// src/lib/firebase.ts
import { initializeApp, getApps, getApp } from "firebase/app";
import { getAuth } from "firebase/auth";
const firebaseConfig = {
apiKey: import.meta.env.PUBLIC_FIREBASE_API_KEY,
authDomain: import.meta.env.PUBLIC_FIREBASE_AUTH_DOMAIN,
projectId: import.meta.env.PUBLIC_FIREBASE_PROJECT_ID,
};
const app = getApps().length ? getApp() : initializeApp(firebaseConfig);
export const auth = getAuth(app);
El chequeo getApps().length ? getApp() : initializeApp(...) evita el error de “Firebase App named ‘[DEFAULT]’ already exists”, que ocurre si el módulo se vuelve a evaluar por el hot reload de Astro en desarrollo.
Paso 3: Solo usar Firebase dentro de componentes con directivas de cliente
En Astro, un componente de React o Vue no se hidrata en el navegador a menos que se le agregue una directiva como client:load. Cualquier código que use auth de Firebase debe vivir dentro de uno de esos componentes, nunca en el frontmatter del .astro:
---
import LoginForm from "../components/LoginForm.tsx";
---
<LoginForm client:load />
Y dentro de LoginForm.tsx, ya puedo importar auth sin problemas, porque ese componente solo se ejecuta en el navegador después de hidratarse.
Paso 4: Verificar el estado de sesión sin bloquear el render
Para saber si el usuario ya tiene sesión iniciada, usé onAuthStateChanged dentro de un useEffect, con un estado de carga mientras Firebase resuelve la sesión:
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setUser(user);
setLoading(false);
});
return () => unsubscribe();
}, []);
Sin ese estado de carga, la UI parpadea mostrando “no autenticado” por una fracción de segundo antes de que Firebase confirme la sesión real.
Con estos cuatro pasos, Firebase y Astro conviven sin errores de hidratación ni de variables de entorno perdidas.
Si quieres ver un proyecto real usando esta configuración, puedes revisar mi perfil de GitHub: https://github.com/tuerre