The modular design of the Firebase JS SDK gives you much greater control over how your app is built. This flexibility allows you to tailor your dependencies for your platform and optimize your bundle size by stripping away features that you don’t need.
There are two ways to initialize the Auth library: the getAuth()
function and
the initializeAuth()
function. The first, getAuth()
, provides everything
your app needs in order to take advantage of all the features the Auth library
has to offer. The downside is that it pulls in a lot of code that is potentially
unused by your app. It also may pull in code that is simply unsupported on the
platform you’re targeting, leading to errors. To avoid these problems, you can
use initializeAuth()
, which takes a map of dependencies. The getAuth()
function simply calls initializeAuth()
with all of the dependencies specified.
To illustrate, here is the equivalent to getAuth()
on browser environments:
import {initializeAuth, browserLocalPersistence, browserPopupRedirectResolver, browserSessionPersistence, indexedDBLocalPersistence} from "firebase/auth";
import {initializeApp} from "firebase/app";
const app = initializeApp({/** Your app config */});
const auth = initializeAuth(app, {
persistence: [indexedDBLocalPersistence, browserLocalPersistence, browserSessionPersistence],
popupRedirectResolver: browserPopupRedirectResolver,
});
Tailoring your dependencies
Not all apps use the signInWithPopup
or signInWithRedirect
family of
functions. Many apps won’t need the flexibility that indexedDB
provides, or
won’t need the ability to support both indexedDB
and localStorage
should one
not be available. In these cases, the default getAuth()
includes a lot of
unused code that increases bundle sizes for no reason. Instead, these apps can
tailor their dependencies. For example, if your app only uses email link
authentication and localStorage is sufficient (because you’re not using web or
service worker scripts), you can strip a lot of code bloat by initializing Auth
like this:
import {initializeAuth, browserLocalPersistence} from "firebase/auth";
import {initializeApp} from "firebase/app";
const app = initializeApp({/** Your app config */});
const auth = initializeAuth(app, {
persistence: browserLocalPersistence,
// No popupRedirectResolver defined
});
With this code, you’ve removed three large dependencies that your app does not need, significantly cutting down the amount of bandwidth your users use whenever they visit your site.
Platform-specific considerations
In many cases, you need to manually define the Auth dependencies in order to
avoid errors on initialization. The getAuth()
function assumes a specific
platform. For the default entry point, that is a browser environment and for the
Cordova entry point, that’s a Cordova environment. But sometimes the needs of
your particular application clash with these assumptions. For web and service
worker scripts, for example, the default getAuth()
implementation pulls in
code that reads from the window
object, which will cause errors. In those
cases, it is necessary to tailor your dependencies. The following code is
appropriate to initialize the Auth library in a service worker context:
import {initializeAuth, indexedDBLocalPersistence} from "firebase/auth";
import {initializeApp} from "firebase/app";
const app = initializeApp({/** Your app config */});
const auth = initializeAuth(app, {
persistence: indexedDBLocalPersistence,
// No popupRedirectResolver defined
});
This code instructs Auth to initialize with indexedDB
persistence (which is
available in worker contexts) and omits the popupRedirectResolver
dependency,
which assumes a DOM context is available.
There are other reasons you might manually define dependencies on certain
platforms. By defining the popupRedirectResolver
field in Auth initialization,
in some cases the library will perform additional work on initialization. On
mobile browsers, the library will automatically open an iframe to your Auth
domain preemptively. This is done to make the experience seamless for most
users, but it can impact performance by loading additional code right when the
app starts. This behavior can be avoided by utilizing initializeAuth()
and
manually passing the browserPopupRedirectResolver
dependency to the functions
that need it:
import {initializeAuth, browserLocalPersistence, browserPopupRedirectResolver, indexedDBLocalPersistence, signInWithRedirect, GoogleAuthProvider} from "firebase/auth";
import {initializeApp} from "firebase/app";
const app = initializeApp({/** Your app config */});
const auth = initializeAuth(app, {
persistence: [indexedDBLocalPersistence, browserLocalPersistence],
});
// Later
signInWithRedirect(auth, new GoogleAuthProvider(), browserPopupRedirectResolver);
If we had provided browserPopupRedirectResolver
in the dependencies to
initializeAuth()
, the third parameter in the call to signInWithRedirect()
would not have been needed. But by moving that dependency to the call to
signInWithRedirect()
directly, the initial performance hit during
initialization is removed. There are tradeoffs that come with moving the
dependency, but the important part is that you are able to make decisions about
those tradeoffs by manually initializing the library.
When to use custom initialization
To recap, custom initialization gives you far greater control over your app’s
usage of the Auth SDK. The standard getAuth()
function is good for getting
started and serves most use cases. For most apps, getAuth()
may be all you
need. But there are many reasons why you may want (or need) to switch to manual
dependency management:
- For apps where bundle size and load times are extremely important, custom Auth initialization can potentially cut down on many kilobytes of data. It can also cut down on initial load times by moving dependencies to time of use instead of time of initialization.
- For code that runs in non-DOM contexts (like web and service workers),
initializeAuth()
must be used to avoid errors.