Typed React hooks for Firebase, one hook per flow
Each hook runs a whole flow end to end and holds its own loading, error and success state. Zero dependencies — firebase and react stay peers.
The same sign-in, twice
Email and password, with a server session minted from the ID token and errors surfaced to the UI.
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
async function login(email: string, password: string) {
setLoading(true);
setError(null);
try {
const cred = await signInWithEmailAndPassword(
auth,
email,
password,
);
const idToken = await cred.user.getIdToken();
await createSession(idToken);
return cred;
} catch (e) {
setError(toMessage(e));
} finally {
setLoading(false);
}
}const { login, loading, error } = useLogin(auth, {
onIdToken: (idToken) => createSession(idToken),
});
const result = await login(email, password);
if (result.success) router.push("/dashboard");And it does more: if createSession throws, the sign-in aborts rather than leaving a signed-in user with no server session.
What you get
Six decisions that shape every hook in the package.
Whole flows, not single calls
usePhoneSignIn builds and tears down the reCAPTCHA verifier. useOAuthSignIn finishes a redirect when the page returns. useEmailLinkSignIn asks for the address instead of calling window.prompt.
Failures are values
Actions never throw. A failure carries Firebase’s own code and the untouched original error, so you branch on a result instead of wrapping every call in try/catch.
Server sessions built in
onIdToken hands you a fresh ID token as part of the sign-in, not after it. Throw inside it and the sign-in aborts, so a user can’t land on a protected page without a server session.
Nothing withheld
Sign-ins hand back Firebase’s raw UserCredential. Error messages stay exactly as Firebase wrote them unless you opt into formatting.
Configure once, override anywhere
Session callbacks, action-code settings and error wording live on the provider. Any hook can override them, or opt out entirely with null.
Reauthentication handled
Pass currentPassword to a sensitive operation and the recent-sign-in check happens first. Omit it, and auth/requires-recent-login reaches you to handle your own way.
Twenty hooks, one contract
Every Firebase Auth client flow, each following the same shape — so learning one is learning the rest.
Signing in and out8
One import per service
An app only carries the services it uses. The most-used ship first.
- Core
@timonwa/firebase-hooksAvailable - Auth
@timonwa/firebase-hooks/authAvailable - Firestore
@timonwa/firebase-hooks/firestoreComing soon - Storage
@timonwa/firebase-hooks/storageComing soon - Cloud Functions
@timonwa/firebase-hooks/functionsComing soon