Getting started
Install the package and sign a user in.
Install
pnpm add @timonwa/firebase-hooks firebaseRequires React 18 or 19 and Firebase 11 or 12. Both are peer dependencies, so you control the versions.
Create your Auth instance
Every hook needs a Firebase Auth instance. That object comes from the firebase package, not from this one — you create it once and import it wherever you need it. There is no global and no hidden singleton.
import { getApps, initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
const config = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY!,
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN!,
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID!,
appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID!,
};
// initializeApp throws on a duplicate name, which hot reloading would cause.
const app = getApps()[0] ?? initializeApp(config);
export const auth = getAuth(app);Those four values come from the Firebase console under Project settings → Your apps → SDK setup and configuration. They are public by design — they identify your project, they don't authorise anything.
auth is what the rest of this page means whenever it says auth.
Add the provider
Wrap your app once. AuthProvider watches Firebase for you, so any component can ask who is signed in and re-render when that changes — and every hook below it takes its auth and its defaults from here.
import { AuthProvider } from '@timonwa/firebase-hooks/auth';
import { auth } from '@/lib/firebase';
<AuthProvider auth={auth} onIdToken={(idToken) => createSession(idToken)}>
{children}
</AuthProvider>;import { useAuth } from '@timonwa/firebase-hooks/auth';
function Navbar() {
const { firebaseUser, isLoading } = useAuth();
if (isLoading) return null;
return firebaseUser ? <Avatar email={firebaseUser.email} /> : <SignInLink />;
}Without the provider, a sign-in on one page can't tell your navbar on another — nothing is subscribed. useAuth is the only hook that requires it, and it throws rather than pretending nobody is signed in.
Sign a user in
Nothing to pass: the provider supplies both the instance and the onIdToken you set on it.
import { useLogin } from '@timonwa/firebase-hooks/auth';
function LoginForm() {
const { login, loading, error } = useLogin();
async function onSubmit(email: string, password: string) {
const result = await login(email, password);
if (result.success) router.push('/dashboard');
}
}login resolves to a result rather than throwing, so a failed sign-in is a value you read.
Without a provider
Every hook except useAuth works without one — pass the instance yourself. An explicit auth also wins over the provider's, which is how you reach a second Firebase project:
import { auth } from '@/lib/firebase';
const { login } = useLogin(auth, {
onIdToken: (idToken) => createSession(idToken),
});