npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dc-auth

v1.2.1

Published

Autenticación universal con Firebase y Zustand para Next.js 15 - Configurable con callbacks

Readme

🔐 dc-auth

Librería de autenticación moderna y universal para Next.js 15 con Firebase y Zustand. Diseñada específicamente para el App Router de Next.js con soporte completo para Server Components y Client Components.

npm version License: MIT

✨ Características

  • Next.js 15 Ready - Totalmente compatible con App Router
  • 🔥 Firebase Auth - Autenticación con Email/Password y Google
  • 🎯 Type-Safe - TypeScript completo con tipos exportados
  • SSR Compatible - Inicialización segura en cliente/servidor
  • 🪝 Custom Hooks - Hook useDCAuth con todas las funciones
  • 🛡️ Protected Routes - Componente para rutas protegidas
  • 🔄 Estado Global - Zustand para manejo de estado optimizado
  • ⚙️ Configurable - Múltiples proyectos/entornos (v1.2.0)
  • 🎣 Callbacks - onLogin, onLogout, onError (v1.2.0)
  • Error Handling - Manejo de errores integrado (v1.2.0)
  • 👤 Update Profile - Actualizar displayName y photoURL (v1.2.0)
  • 📦 Zero Config - Solo configura variables de entorno (o usa config personalizada)

📦 Instalación

npm install dc-auth firebase zustand
# o
pnpm add dc-auth firebase zustand
# o
yarn add dc-auth firebase zustand

🚀 Configuración

1. Variables de Entorno

Crea un archivo .env.local en la raíz de tu proyecto Next.js:

NEXT_PUBLIC_FIREBASE_API_KEY=tu_api_key
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=tu_auth_domain
NEXT_PUBLIC_FIREBASE_PROJECT_ID=tu_project_id
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET=tu_storage_bucket
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID=tu_messaging_sender_id
NEXT_PUBLIC_FIREBASE_APP_ID=tu_app_id

2. Configurar el Provider

⚠️ IMPORTANTE: Para preservar SSR en Next.js 15, usa el patrón recomendado:

✅ Opción 1: Layout Híbrido (Recomendado)

Mantén tu layout principal como Server Component e importa solo el provider de cliente:

// app/layout.tsx (Server Component)
import { ClientProviders } from "dc-auth";
import "./globals.css";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="es">
      <body>
        <ClientProviders>
          {children}
        </ClientProviders>
      </body>
    </html>
  );
}

✅ Opción 2: Provider Selectivo

Si solo algunas páginas necesitan autenticación, envuelve solo esas secciones:

// app/layout.tsx (Server Component - SIN provider)
import "./globals.css";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="es">
      <body>{children}</body>
    </html>
  );
}
// app/(authenticated)/layout.tsx (Client layout para rutas privadas)
"use client";
import { DCAuth } from "dc-auth";

export default function AuthenticatedLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return <DCAuth>{children}</DCAuth>;
}

Esta estructura mantiene el SSR en páginas públicas y solo usa Client Components donde es necesario.

📘 Guía Completa de SSR: Lee SSR_GUIDE.md para ejemplos detallados y mejores prácticas.

3. Configuración Avanzada (v1.2.0)

Si necesitas configuración personalizada o callbacks:

// app/layout.tsx
import { ClientProviders } from "dc-auth";

const firebaseConfig = {
  apiKey: "AIzaSy...",
  authDomain: "my-app.firebaseapp.com",
  projectId: "my-app",
  // ...resto de config
};

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <ClientProviders
          config={firebaseConfig}  // Config personalizada (opcional)
          callbacks={{
            onLogin: (user) => {
              console.log("Usuario logueado:", user.email);
              // Sincronizar con backend, analytics, etc.
            },
            onLogout: () => {
              console.log("Usuario cerró sesión");
              // Limpiar datos, redirect, etc.
            },
            onError: (error) => {
              console.error("Error de auth:", error);
              // Mostrar toast, enviar a Sentry, etc.
            },
          }}
        >
          {children}
        </ClientProviders>
      </body>
    </html>
  );
}

📘 Guía Completa de Uso: Lee USAGE_GUIDE.md para 8+ ejemplos prácticos con callbacks, analytics, Sentry, etc.

📖 Uso

Hook useDCAuth

El hook principal para interactuar con la autenticación:

"use client";
import { useDCAuth } from "dc-auth";

export default function LoginPage() {
  const { 
    user,              // Usuario actual (Firebase User | null)
    loading,           // Estado de carga
    error,             // Error actual (si hay) - v1.2.0
    clearError,        // Limpiar error - v1.2.0
    isAuthenticated,   // Boolean: ¿está autenticado?
    loginWithEmail,    // Función de login con email
    loginWithGoogle,   // Función de login con Google
    logout,            // Función de logout
    updateProfile      // Actualizar perfil - v1.2.0
  } = useDCAuth();

  const handleLogin = async () => {
    try {
      await loginWithEmail("[email protected]", "password123");
      console.log("Login exitoso!");
    } catch (error) {
      console.error("Error:", error);
    }
  };

  const handleGoogleLogin = async () => {
    try {
      await loginWithGoogle();
      console.log("Login con Google exitoso!");
    } catch (error) {
      console.error("Error:", error);
    }
  };

  if (loading) return <div>Cargando...</div>;

  return (
    <div>
      {isAuthenticated ? (
        <div>
          <p>Bienvenido {user?.email}</p>
          <button onClick={logout}>Cerrar Sesión</button>
        </div>
      ) : (
        <div>
          <button onClick={handleLogin}>Login con Email</button>
          <button onClick={handleGoogleLogin}>Login con Google</button>
        </div>
      )}
    </div>
  );
}

Componente ProtectedRoute

Protege rutas que requieren autenticación:

"use client";
import { ProtectedRoute } from "dc-auth";

export default function DashboardPage() {
  return (
    <ProtectedRoute redirectTo="/login">
      <div>
        <h1>Dashboard Privado</h1>
        <p>Solo usuarios autenticados pueden ver esto</p>
      </div>
    </ProtectedRoute>
  );
}

Ejemplo de Login Completo

"use client";
import { useDCAuth } from "dc-auth";
import { useState, useEffect } from "react";
import { useRouter } from "next/navigation";

export default function LoginPage() {
  const { loginWithEmail, loginWithGoogle, isAuthenticated, loading } = useDCAuth();
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [error, setError] = useState("");
  const router = useRouter();

  // Redirigir cuando el usuario se autentique
  useEffect(() => {
    if (!loading && isAuthenticated) {
      router.push("/dashboard");
    }
  }, [isAuthenticated, loading, router]);

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setError("");
    
    try {
      await loginWithEmail(email, password);
      // La redirección se maneja en el useEffect
    } catch (err: any) {
      setError(err.message);
    }
  };

  const handleGoogleLogin = async () => {
    try {
      await loginWithGoogle();
      // La redirección se maneja en el useEffect
    } catch (err: any) {
      setError(err.message);
    }
  };

  if (loading) {
    return <div>Cargando...</div>;
  }

  if (isAuthenticated) {
    return null; // Redirigiendo...
  }

  return (
    <div className="login-container">
      <h1>Iniciar Sesión</h1>
      
      <form onSubmit={handleSubmit}>
        <input
          type="email"
          placeholder="Email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
          required
        />
        <input
          type="password"
          placeholder="Contraseña"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          required
        />
        <button type="submit">Entrar</button>
      </form>

      <button onClick={handleGoogleLogin}>
        Continuar con Google
      </button>

      {error && <p className="error">{error}</p>}
    </div>
  );
}

Ejemplo de Dashboard Protegido

"use client";
import { ProtectedRoute, useDCAuth } from "dc-auth";
import { useRouter } from "next/navigation";

export default function DashboardPage() {
  const { user, logout } = useDCAuth();
  const router = useRouter();

  const handleLogout = async () => {
    await logout();
    router.push("/login");
  };

  return (
    <ProtectedRoute redirectTo="/login">
      <div className="dashboard">
        <header>
          <h1>Dashboard</h1>
          <div>
            <span>{user?.email}</span>
            <button onClick={handleLogout}>Cerrar Sesión</button>
          </div>
        </header>
        <main>
          <h2>Bienvenido, {user?.displayName || user?.email}</h2>
          <p>Este contenido es privado</p>
        </main>
      </div>
    </ProtectedRoute>
  );
}

Acceso sin Hooks (window.dcAuth)

También puedes acceder a las funciones de autenticación directamente desde window.dcAuth:

"use client";

function MyComponent() {
  const handleLogin = () => {
    if (typeof window !== "undefined" && window.dcAuth) {
      window.dcAuth.loginWithEmail("[email protected]", "password123")
        .then(() => console.log("Login exitoso"))
        .catch((err) => console.error(err));
    }
  };

  return <button onClick={handleLogin}>Login</button>;
}

⚠️ Nota: Este método es útil para casos especiales, pero se recomienda usar el hook useDCAuth para mejor experiencia de desarrollo con TypeScript.

🎯 API Reference

useDCAuth()

Hook principal que retorna:

| Propiedad | Tipo | Descripción | |-----------|------|-------------| | user | User \| null | Usuario actual de Firebase | | loading | boolean | Estado de carga de la autenticación | | isAuthenticated | boolean | true si hay un usuario autenticado | | loginWithEmail | (email: string, password: string) => Promise<void> | Login con email/password | | loginWithGoogle | () => Promise<void> | Login con Google | | logout | () => Promise<void> | Cerrar sesión |

<DCAuth>

Componente Provider que debe envolver tu aplicación.

Props:

  • children: React.ReactNode - Componentes hijos

<ProtectedRoute>

Componente para proteger rutas que requieren autenticación.

Props: | Prop | Tipo | Default | Descripción | |------|------|---------|-------------| | children | React.ReactNode | - | Contenido protegido | | redirectTo | string | "/login" | Ruta a la que redirigir si no está autenticado |

Tipos TypeScript

import type { DCAuthConfig, DCAuthState, DCAuthMethods } from "dc-auth";

// DCAuthConfig - Configuración de Firebase
interface DCAuthConfig {
  apiKey: string;
  authDomain: string;
  projectId: string;
  storageBucket: string;
  messagingSenderId: string;
  appId: string;
}

// DCAuthState - Estado de autenticación
interface DCAuthState {
  user: User | null;
  loading: boolean;
  setUser: (user: User | null) => void;
  setLoading: (loading: boolean) => void;
}

// DCAuthMethods - Métodos de autenticación
interface DCAuthMethods {
  loginWithEmail: (email: string, password: string) => Promise<void>;
  loginWithGoogle: () => Promise<void>;
  logout: () => Promise<void>;
}

🏗️ Arquitectura y SSR

Preservando Server-Side Rendering

dc-auth está diseñado para no romper SSR. Sigue estas prácticas:

✅ Páginas Públicas Permanecen como Server Components

// app/page.tsx - Server Component ✅
export default function HomePage() {
  return (
    <div>
      <h1>Bienvenido</h1>
      <p>Esta página se renderiza en el servidor</p>
    </div>
  );
}

✅ Solo Páginas con Auth usan "use client"

// app/dashboard/page.tsx - Client Component (usa auth)
"use client";
import { useDCAuth } from "dc-auth";

export default function DashboardPage() {
  const { user } = useDCAuth();
  return <div>Hola {user?.email}</div>;
}

✅ Usa Route Groups para Organizar

app/
├── layout.tsx              # Server Component (root)
├── page.tsx                # Server Component (home pública)
├── about/
│   └── page.tsx           # Server Component
├── (auth)/                 # Route group para páginas con auth
│   ├── layout.tsx         # Client Component con DCAuth
│   ├── dashboard/
│   │   └── page.tsx       # Client Component
│   └── profile/
│       └── page.tsx       # Client Component
└── (public)/
    └── pricing/
        └── page.tsx       # Server Component

Ventajas de este Patrón

  • SEO optimizado - Páginas públicas con SSR completo
  • Performance - Solo carga client JS donde es necesario
  • Flexibilidad - Mezcla server y client components
  • Cacheable - Next.js puede cachear server components

🔧 Características Avanzadas

Manejo de Errores

Todas las funciones de autenticación lanzan errores que puedes capturar:

try {
  await loginWithEmail(email, password);
} catch (error) {
  if (error.code === "auth/user-not-found") {
    console.log("Usuario no encontrado");
  } else if (error.code === "auth/wrong-password") {
    console.log("Contraseña incorrecta");
  } else {
    console.log("Error:", error.message);
  }
}

Persistencia de Sesión

Firebase Auth maneja automáticamente la persistencia de sesión. El usuario permanecerá autenticado entre recargas de página.

SSR y Hydration

La librería está diseñada para Next.js 15 App Router:

  • ✅ Inicialización segura solo en cliente
  • ✅ Sin errores de hydration
  • ✅ Compatible con Server Components (aunque el auth es client-side)

🏗️ Estructura del Proyecto

dc-auth/
├── src/
│   ├── DCAuthProvider.tsx    # Provider principal
│   ├── ProtectedRoute.tsx    # Componente de rutas protegidas
│   ├── hooks/
│   │   └── useDCAuth.ts      # Hook principal
│   ├── store.ts              # Store de Zustand
│   ├── types.ts              # Tipos TypeScript
│   └── index.ts              # Exports públicos
├── dist/                     # Build output
├── package.json
├── tsconfig.json
└── tsup.config.ts

🤝 Contribuir

Las contribuciones son bienvenidas! Por favor:

  1. Fork el proyecto
  2. Crea una rama (git checkout -b feature/amazing-feature)
  3. Commit tus cambios (git commit -m 'Add amazing feature')
  4. Push a la rama (git push origin feature/amazing-feature)
  5. Abre un Pull Request

📝 Changelog

v1.0.0

  • ✅ Soporte completo para Next.js 15
  • ✅ Autenticación con Firebase (Email/Password y Google)
  • ✅ Hook useDCAuth con TypeScript
  • ✅ Componente ProtectedRoute
  • ✅ Manejo seguro de SSR
  • ✅ Singleton pattern para Firebase
  • ✅ Manejo de errores mejorado

🔧 Troubleshooting

Error: "Cannot update a component while rendering a different component"

Si ves este error:

Cannot update a component (Router) while rendering a different component (LoginPage).

Causa: Estás llamando a router.push() durante el render, no en un efecto o evento.

Solución: Mueve la redirección a un useEffect:

// ❌ MAL - Durante el render
if (isAuthenticated) {
  router.push("/dashboard");
  return null;
}

// ✅ BIEN - En useEffect
useEffect(() => {
  if (!loading && isAuthenticated) {
    router.push("/dashboard");
  }
}, [isAuthenticated, loading, router]);

Error: "You're importing a component that needs useEffect"

Si ves este error al importar desde dc-auth:

You're importing a component that needs `useEffect`. 
This React Hook only works in a Client Component.

Solución: Asegúrate de estar usando la versión más reciente de dc-auth:

npm update dc-auth
# o
npm install dc-auth@latest

La versión 1.0.1+ incluye la directiva "use client" en los bundles compilados.

Error: "Firebase app already exists"

Si ves este error, es probable que estés inicializando Firebase múltiples veces. Asegúrate de:

  1. Usar ClientProviders o DCAuth solo una vez en tu árbol de componentes
  2. No inicializar Firebase manualmente en otro lugar
  3. Usar la versión 1.0.0+ que incluye el singleton pattern

Páginas no tienen SSR

Si tus páginas públicas no tienen SSR:

  1. Verifica que tu app/layout.tsx no tenga "use client"
  2. Usa ClientProviders en lugar de DCAuth en el layout raíz
  3. Las páginas que no usan useDCAuth deben ser Server Components (sin "use client")

Lee SSR_GUIDE.md para más detalles.

🔮 Roadmap y Escalabilidad

Esta librería está en desarrollo activo. Hemos identificado áreas clave de mejora para hacerla production-ready.

📊 Análisis Completo

🎯 Próximas Features (v1.1.0)

  • [ ] Registro de usuarios (signUp)
  • [ ] Reset de contraseña
  • [ ] Verificación de email
  • [ ] Actualización de perfil
  • [ ] Provider configurable con callbacks
  • [ ] Error state mejorado
  • [ ] Más providers (GitHub, Facebook, Twitter)
  • [ ] Hooks especializados

💡 Contribuciones

¿Quieres contribuir? Lee CONTRIBUTING.md y IMPLEMENTATION_PLAN.md.

📄 Licencia

MIT © Kevin Del Cid

🔗 Links


Hecho con ❤️ para la comunidad Next.js