+{% endmacro %}
diff --git a/site/src/analytics/analytics.11tydata.json b/site/src/analytics/analytics.11tydata.json
new file mode 100644
index 000000000..b924da1f3
--- /dev/null
+++ b/site/src/analytics/analytics.11tydata.json
@@ -0,0 +1,4 @@
+{
+ "layout": "guide.njk",
+ "tags": "guides"
+}
diff --git a/site/src/analytics/getting-started.md b/site/src/analytics/getting-started.md
new file mode 100644
index 000000000..33753cf89
--- /dev/null
+++ b/site/src/analytics/getting-started.md
@@ -0,0 +1,140 @@
+---
+title: Getting started
+eleventyNavigation:
+ key: Getting started
+ parent: Analytics
+---
+
+## Google Analytics
+
+`AngularFireAnalytics` dynamically imports the `firebase/analytics` library and provides a promisified version of the [Firebase Analytics SDK (`firebase.analytics.Analytics`)](https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics.html).
+
+## API Summary
+
+```ts
+class AngularFireAnalytics {
+ updateConfig(options: {[key:string]: any}): Promise;
+
+ // from firebase.analytics() proxy:
+ logEvent(eventName: string, eventParams?: {[key: string]: any}, options?: analytics.AnalyticsCallOptions): Promise;
+ setCurrentScreen(screenName: string, options?: analytics.AnalyticsCallOptions): Promise;
+ setUserId(id: string, options?: analytics.AnalyticsCallOptions): Promise;
+ setUserProperties(properties: analytics.CustomParams, options?: analytics.AnalyticsCallOptions): Promise;
+ setAnalyticsCollectionEnabled(enabled: boolean): Promise;
+ app: Promise;
+}
+
+COLLECTION_ENABLED = InjectionToken;
+APP_VERSION = InjectionToken;
+APP_NAME = InjectionToken;
+DEBUG_MODE = InjectionToken;
+CONFIG = InjectionToken;
+```
+
+## Usage
+
+```ts
+import { AngularFireAnalyticsModule } from '@angular/fire/analytics';
+
+@NgModule({
+ imports: [
+ AngularFireModule.initializeApp(environment.firebase),
+ AngularFireAnalyticsModule
+ ]
+})
+export class AppModule { }
+```
+
+`AngularFireAnalyticsModule` will dynamically import and configure `firebase/analytics`. A `page_view` event will automatically be logged (see `CONFIG` below if you wish to disable this behavior.)
+
+In your component you can then dependency inject `AngularFireAnalytics` and make calls against the SDK:
+
+```ts
+import { AngularFireAnalytics } from '@angular/fire/analytics';
+
+constructor(analytics: AngularFireAnalytics) {
+ analytics.logEvent('custom_event', { ... });
+}
+```
+
+## Tracking Screen Views
+
+You can log [`screen_view` events](https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics.html#parameters_10) yourself of course, but AngularFire provides the `ScreenTrackingService` which automatically integrates with the Angular Router to provide Firebase with screen view tracking. You simply can integrate like so:
+
+```ts
+import { AngularFireAnalyticsModule, ScreenTrackingService } from '@angular/fire/analytics';
+
+@NgModule({
+ imports: [
+ AngularFireModule.initializeApp(environment.firebase),
+ AngularFireAnalyticsModule
+ ],
+ providers: [
+ ScreenTrackingService
+ ]
+})
+export class AppModule { }
+```
+
+`AngularFireAnalyticsModule` will initialize `ScreenTrackingService` if it is provided.
+
+## Tracking User Identifiers
+
+To enrich your Analytics data you can track the currently signed in user by setting [`setuserid`](https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics.html#setuserid) and [`setUserProperties`](https://firebase.google.com/docs/reference/js/firebase.analytics.Analytics.html#set-user-properties). AngularFire provides a `UserTrackingService` which will dynamically import `firebase/auth`, monitor for changes in the logged in user, and call `setuserid` for you automatically.
+
+
+```ts
+import { AngularFireAnalyticsModule, UserTrackingService } from '@angular/fire/analytics';
+
+@NgModule({
+ imports: [
+ AngularFireModule.initializeApp(environment.firebase),
+ AngularFireAnalyticsModule
+ ],
+ providers: [
+ UserTrackingService
+ ]
+})
+export class AppModule { }
+```
+
+`AngularFireAnalyticsModule` will initialize `UserTrackingService` if it is provided.
+
+## Configuration with Dependency Injection
+
+Using the `CONFIG` DI Token (*default: {}*) will allow you to configure Google Analytics. E.g, you could skip sending the initial `page_view` event, anonymize IP addresses, and disallow ads personalization signals for all events like so:
+
+```ts
+import { AngularFireAnalyticsModule, CONFIG } from '@angular/fire/analytics';
+
+@NgModule({
+ imports: [
+ AngularFireModule.initializeApp(environment.firebase),
+ AngularFireAnalyticsModule
+ ],
+ providers: [
+ { provide: CONFIG, useValue: {
+ send_page_view: false,
+ allow_ad_personalization_signals: false,
+ anonymize_ip: true
+ } }
+ ]
+})
+export class AppModule { }
+```
+
+See the gtag.js documentation to learn of the different configuration options at your disposal.
+
+## Use DebugView
+
+To use [DebugView in Analytics](https://console.firebase.google.com/project/_/analytics/debugview) set `DEBUG_MODE` to `true` (*default: false*).
+
+## Track deployments
+
+If you provide `APP_NAME` and `APP_VERSION` (*default: undefined*) you will be able to [track version adoption](https://console.firebase.google.com/project/_/analytics/latestrelease) of your PWA.
+
+## Disable collection
+
+If you set `COLLECTION_ENABLED` (*default: true*) to `false` then analytics collection will be disabled for this app on this device. To opt back in to analytics collection you could then call `setAnalyticsCollectionEnabled(true)`.
+
+Putting these APIs to use with cookies would allow you to create a flexible analytics collection scheme that would respect your user's preferences and data collection policies.
diff --git a/site/src/analytics/index.md b/site/src/analytics/index.md
new file mode 100644
index 000000000..4bcfd0e99
--- /dev/null
+++ b/site/src/analytics/index.md
@@ -0,0 +1,6 @@
+---
+eleventyNavigation:
+ key: Analytics
+ order: 5
+---
+
diff --git a/site/src/assets/GoogleSans-Bold.woff2 b/site/src/assets/GoogleSans-Bold.woff2
new file mode 100644
index 000000000..f69d92101
Binary files /dev/null and b/site/src/assets/GoogleSans-Bold.woff2 differ
diff --git a/site/src/assets/GoogleSans-Medium.woff2 b/site/src/assets/GoogleSans-Medium.woff2
new file mode 100644
index 000000000..ab01e3bbb
Binary files /dev/null and b/site/src/assets/GoogleSans-Medium.woff2 differ
diff --git a/site/src/assets/GoogleSans-Regular.woff2 b/site/src/assets/GoogleSans-Regular.woff2
new file mode 100644
index 000000000..7803436c9
Binary files /dev/null and b/site/src/assets/GoogleSans-Regular.woff2 differ
diff --git a/site/src/assets/Roboto-900.woff2 b/site/src/assets/Roboto-900.woff2
new file mode 100644
index 000000000..802499d3f
Binary files /dev/null and b/site/src/assets/Roboto-900.woff2 differ
diff --git a/site/src/assets/Roboto-Italic.woff2 b/site/src/assets/Roboto-Italic.woff2
new file mode 100644
index 000000000..2741d4f08
Binary files /dev/null and b/site/src/assets/Roboto-Italic.woff2 differ
diff --git a/site/src/assets/Roboto-Regular.woff2 b/site/src/assets/Roboto-Regular.woff2
new file mode 100644
index 000000000..1a5370151
Binary files /dev/null and b/site/src/assets/Roboto-Regular.woff2 differ
diff --git a/site/src/assets/RobotoMono-Regular.woff2 b/site/src/assets/RobotoMono-Regular.woff2
new file mode 100644
index 000000000..ed384d22f
Binary files /dev/null and b/site/src/assets/RobotoMono-Regular.woff2 differ
diff --git a/site/src/assets/corner.svg b/site/src/assets/corner.svg
new file mode 100644
index 000000000..ba786e975
--- /dev/null
+++ b/site/src/assets/corner.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/site/src/assets/firebase-logo.svg b/site/src/assets/firebase-logo.svg
new file mode 100644
index 000000000..c2313a7a7
--- /dev/null
+++ b/site/src/assets/firebase-logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/site/src/auth/auth.11tydata.json b/site/src/auth/auth.11tydata.json
new file mode 100644
index 000000000..b924da1f3
--- /dev/null
+++ b/site/src/auth/auth.11tydata.json
@@ -0,0 +1,4 @@
+{
+ "layout": "guide.njk",
+ "tags": "guides"
+}
diff --git a/site/src/auth/getting-started.md b/site/src/auth/getting-started.md
new file mode 100644
index 000000000..cf3edae22
--- /dev/null
+++ b/site/src/auth/getting-started.md
@@ -0,0 +1,55 @@
+---
+title: Getting started
+eleventyNavigation:
+ key: Getting started
+ parent: Auth
+---
+
+## Using AngularFireAuth
+
+`AngularFireAuth.user` provides you an `Observable` to monitor your application's authentication State.
+
+`AngularFireAuth` promise proxies an initialized
+`firebase.auth.Auth` instance, allowing you to log users in, out, etc. [See
+the Firebase docs for more information on what methods are available.](https://firebase.google.com/docs/reference/js/firebase.auth.Auth)
+
+**Example app:**
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFireAuth } from '@angular/fire/auth';
+import firebase from 'firebase/app';
+
+@Component({
+ selector: 'app-root',
+ template: `{%raw%}
+
+
Hello {{ user.displayName }}!
+
+
+
+
Please login.
+
+
+ {%endraw%}`,
+})
+export class AppComponent {
+ constructor(public auth: AngularFireAuth) {
+ }
+ login() {
+ this.auth.signInWithPopup(new firebase.auth.GoogleAuthProvider());
+ }
+ logout() {
+ this.auth.signOut();
+ }
+}
+```
+
+## UI Libraries
+
+- Material Design : [ngx-auth-firebaseui](https://github.com/AnthonyNahas/ngx-auth-firebaseui)
+- Bootstrap : [@firebaseui/ng-bootstrap](https://github.com/firebaseui/ng-bootstrap)
+
+## Cordova
+
+Learn how to [setup Firebase Authentication with Cordova](https://firebase.google.com/docs/auth/web/cordova) in the Firebase Guides.
diff --git a/site/src/auth/index.md b/site/src/auth/index.md
new file mode 100644
index 000000000..9fc2864fe
--- /dev/null
+++ b/site/src/auth/index.md
@@ -0,0 +1,5 @@
+---
+eleventyNavigation:
+ key: Auth
+ order: 3
+---
diff --git a/site/src/auth/route-guards.md b/site/src/auth/route-guards.md
new file mode 100644
index 000000000..1c45b8b2a
--- /dev/null
+++ b/site/src/auth/route-guards.md
@@ -0,0 +1,109 @@
+---
+title: Route guards
+eleventyNavigation:
+ key: Route guards
+ parent: Auth
+---
+
+## Route users with AngularFire guards
+
+`AngularFireAuthGuard` provides a prebuilt [`canActivate` Router Guard](https://angular.io/api/router/CanActivate) using `AngularFireAuth`. By default unauthenticated users are not permitted to navigate to protected routes:
+
+```ts
+import { AngularFireAuthGuard } from '@angular/fire/auth-guard';
+
+export const routes: Routes = [
+ { path: '', component: AppComponent },
+ { path: 'items', component: ItemListComponent, canActivate: [AngularFireAuthGuard] },
+]
+```
+
+## Customizing the behavior
+
+To customize the behavior of `AngularFireAuthGuard`, you can pass an RXJS pipe through the route data's `authGuardPipe` key.
+
+The `auth-guard` module provides the following pre-built pipes:
+
+| Exported pipe | Functionality |
+|-|-|
+| `loggedIn` | The default pipe, rejects if the user is not authenticated. |
+| `isNotAnonymous` | Rejects if the user is anonymous |
+| `emailVerified` | Rejects if the user's email is not verified |
+| `hasCustomClaim(claim)` | Rejects if the user does not have the specified claim |
+| `redirectUnauthorizedTo(redirect)` | Redirect unauthenticated users to a different route |
+| `redirectLoggedInTo(redirect)` | Redirect authenticated users to a different route |
+
+Example use:
+
+```ts
+import { AngularFireAuthGuard, hasCustomClaim, redirectUnauthorizedTo, redirectLoggedInTo } from '@angular/fire/auth-guard';
+
+const adminOnly = () => hasCustomClaim('admin');
+const redirectUnauthorizedToLogin = () => redirectUnauthorizedTo(['login']);
+const redirectLoggedInToItems = () => redirectLoggedInTo(['items']);
+const belongsToAccount = (next) => hasCustomClaim(`account-${next.params.id}`);
+
+export const routes: Routes = [
+ { path: '', component: AppComponent },
+ { path: 'login', component: LoginComponent, canActivate: [AngularFireAuthGuard], data: { authGuardPipe: redirectLoggedInToItems }},
+ { path: 'items', component: ItemListComponent, canActivate: [AngularFireAuthGuard], data: { authGuardPipe: redirectUnauthorizedToLogin }},
+ { path: 'admin', component: AdminComponent, canActivate: [AngularFireAuthGuard], data: { authGuardPipe: adminOnly }},
+ { path: 'accounts/:id', component: AdminComponent, canActivate: [AngularFireAuthGuard], data: { authGuardPipe: belongsToAccount }}
+];
+```
+
+Use the provided `canActivate` helper and spread syntax to make your routes more readable:
+
+```ts
+import { canActivate } from '@angular/fire/auth-guard';
+
+export const routes: Routes = [
+ { path: '', component: AppComponent },
+ { path: 'login', component: LoginComponent, ...canActivate(redirectLoggedInToItems) },
+ { path: 'items', component: ItemListComponent, ...canActivate(redirectUnauthorizedToLogin) },
+ { path: 'admin', component: AdminComponent, ...canActivate(adminOnly) },
+ { path: 'accounts/:id', component: AdminComponent, ...canActivate(belongsToAccount) }
+];
+```
+
+## Compose your own pipes
+
+`AngularFireAuthGuard` pipes are RXJS operators which transform an optional User to a boolean or Array (for redirects). You can easily build your own to customize behavior further:
+
+```ts
+import { map } from 'rxjs/operators';
+
+// This pipe redirects a user to their "profile edit" page or the "login page" if they're unauthenticated
+// { path: 'profile', ...canActivate(redirectToProfileEditOrLogin) }
+const redirectToProfileEditOrLogin = () => map(user => user ? ['profiles', user.uid, 'edit'] : ['login']);
+```
+
+The `auth-guard` modules provides a `customClaims` operator to reduce boiler plate when checking a user's claims:
+
+```ts
+import { pipe } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { customClaims } from '@angular/fire/auth-guard';
+
+// This pipe will only allow users with the editor role to access the route
+// { path: 'articles/:id/edit', component: ArticleEditComponent, ...canActivate(editorOnly) }
+const editorOnly = () => pipe(customClaims, map(claims => claims.role === 'editor'));
+```
+
+## Using router state
+
+`AngularFireAuthGuard` will also accept `AuthPipeGenerator`s which generate `AuthPipe`s given the router state:
+
+```ts
+import { pipe } from 'rxjs';
+import { map } from 'rxjs/operators';
+import { customClaims } from '@angular/fire/auth-guard';
+
+// Only allow navigation to the route if :userId matches the authenticated user's uid
+// { path: 'user/:userId/edit', component: ProfileEditComponent, ...canActivate(onlyAllowSelf) }
+const onlyAllowSelf = (next) => map(user => !!user && next.params.userId === user.uid);
+
+// Only allow navigation to the route if the user has a custom claim matching :accountId
+// { path: 'accounts/:accountId/billing', component: BillingDetailsComponent, ...canActivate(accountAdmin) }
+const accountAdmin = (next) => pipe(customClaims, map(claims => claims[`account-${next.params.accountId}-role`] === 'admin'));
+```
diff --git a/site/src/favicon.ico b/site/src/favicon.ico
new file mode 100644
index 000000000..6c4d1e73c
Binary files /dev/null and b/site/src/favicon.ico differ
diff --git a/site/src/firestore/collections.md b/site/src/firestore/collections.md
new file mode 100644
index 000000000..9ba924277
--- /dev/null
+++ b/site/src/firestore/collections.md
@@ -0,0 +1,330 @@
+---
+title: Collections
+eleventyNavigation:
+ key: Collections
+ parent: Firestore
+---
+
+## Documents in AngularFirestore
+
+Cloud Firestore is a NoSQL, document-oriented database. Unlike a SQL database, there are no tables or rows. Instead, you store data in *documents*, which are organized into *collections*.
+Each *document* contains a set of key-value pairs. Cloud Firestore is optimized for storing large collections of small documents.
+
+## Using `AngularFirestoreCollection`
+
+The `AngularFirestoreCollection` service is a wrapper around the native Firestore SDK's [`CollectionReference`](https://firebase.google.com/docs/reference/js/firebase.firestore.CollectionReference) and [`Query`](https://firebase.google.com/docs/reference/js/firebase.firestore.Query) types. It is a generic service that provides you with a strongly typed set of methods for manipulating and streaming data. This service is designed for use as an `@Injectable()`.
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFirestore, AngularFirestoreCollection } from '@angular/fire/firestore';
+import { Observable } from 'rxjs';
+
+export interface Item { name: string; }
+
+@Component({
+ selector: 'app-root',
+ template: `
+
+
+ {{ item.name }}
+
+
+ `
+})
+export class AppComponent {
+ private itemsCollection: AngularFirestoreCollection;
+ items: Observable;
+ constructor(private afs: AngularFirestore) {
+ this.itemsCollection = afs.collection('items');
+ this.items = this.itemsCollection.valueChanges();
+ }
+ addItem(item: Item) {
+ this.itemsCollection.add(item);
+ }
+}
+```
+
+The `AngularFirestoreCollection` is a service you use to create streams of the collection and perform data operations on the underyling collection.
+
+## The `DocumentChangeAction` type
+
+With the exception of the `valueChanges()`, each streaming method returns an Observable of `DocumentChangeAction[]`.
+
+A `DocumentChangeAction` gives you the `type` and `payload` properties. The `type` tells when what `DocumentChangeType` operation occured (`added`, `modified`, `removed`). The `payload` property is a `DocumentChange` which provides you important metadata about the change and a `doc` property which is the `DocumentSnapshot`.
+
+```ts
+interface DocumentChangeAction {
+ //'added' | 'modified' | 'removed';
+ type: DocumentChangeType;
+ payload: DocumentChange;
+}
+
+interface DocumentChange {
+ type: DocumentChangeType;
+ doc: DocumentSnapshot;
+ oldIndex: number;
+ newIndex: number;
+}
+
+interface DocumentSnapshot {
+ exists: boolean;
+ ref: DocumentReference;
+ id: string;
+ metadata: SnapshotMetadata;
+ data(): DocumentData;
+ get(fieldPath: string): any;
+}
+```
+
+## Streaming collection data
+
+There are multiple ways of streaming collection data from Firestore.
+
+## `valueChanges({ idField?: string })`
+
+*What is it?* - The current state of your collection. Returns an Observable of data as a synchronized array of JSON objects. All Snapshot metadata is stripped and just the document data is included. Optionally, you can pass an options object with an `idField` key containing a string. If provided, the returned JSON objects will include their document ID mapped to a property with the name provided by `idField`.
+
+*Why would you use it?* - When you just need a list of data. No document metadata is attached to the resulting array which makes it simple to render to a view.
+
+*When would you not use it?* - When you need a more complex data structure than an array.
+
+*Best practices* - Use this method to display data on a page. It's simple but effective. Use `.snapshotChanges()` once your needs become more complex.
+
+#### Example of persisting a Document Id
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFirestore, AngularFirestoreCollection } from '@angular/fire/firestore';
+import { Observable } from 'rxjs';
+
+export interface Item { id: string; name: string; }
+
+@Component({
+ selector: 'app-root',
+ template: `{%raw%}
+
+
+ {{ item.name }}
+
+
+ {%endraw%}`
+})
+export class AppComponent {
+ private itemsCollection: AngularFirestoreCollection;
+ items: Observable;
+ constructor(private readonly afs: AngularFirestore) {
+ this.itemsCollection = afs.collection('items');
+ this.items = this.itemsCollection.valueChanges({ idField: 'customID' });
+ }
+ addItem(name: string) {
+ // Persist a document id
+ const id = this.afs.createId();
+ const item: Item = { id, name };
+ this.itemsCollection.doc(id).set(item);
+ }
+}
+```
+
+### `snapshotChanges()`
+
+*What is it?* - The current state of your collection. Returns an Observable of data as a synchronized array of `DocumentChangeAction[]`.
+
+*Why would you use it?* - When you need a list of data but also want to keep around metadata. Metadata provides you the underyling `DocumentReference`, document id, and array index of the single document. Having the document's id around makes it easier to use data manipulation methods. This method gives you more horsepower with other Angular integrations such as ngrx, forms, and animations due to the `type` property. The `type` property on each `DocumentChangeAction` is useful for ngrx reducers, form states, and animation states.
+
+*When would you not use it?* - When you need a more complex data structure than an array or if you need to process changes as they occur. This array is synchronized with the remote and local changes in Firestore.
+
+*Best practices* - Use an observable operator to transform your data from `.snapshotChanges()`. Don't return the `DocumentChangeAction[]` to the template. See the example below.
+
+#### Example
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFirestore, AngularFirestoreCollection } from '@angular/fire/firestore';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+export interface Shirt { name: string; price: number; }
+export interface ShirtId extends Shirt { id: string; }
+
+@Component({
+ selector: 'app-root',
+ template: `{%raw%}
+
+
+ {{ shirt.name }} is {{ shirt.price }}
+
+
+ {%endraw%}`
+})
+export class AppComponent {
+ private shirtCollection: AngularFirestoreCollection;
+ shirts: Observable;
+ constructor(private readonly afs: AngularFirestore) {
+ this.shirtCollection = afs.collection('shirts');
+ // .snapshotChanges() returns a DocumentChangeAction[], which contains
+ // a lot of information about "what happened" with each change. If you want to
+ // get the data and the id use the map operator.
+ this.shirts = this.shirtCollection.snapshotChanges().pipe(
+ map(actions => actions.map(a => {
+ const data = a.payload.doc.data() as Shirt;
+ const id = a.payload.doc.id;
+ return { id, ...data };
+ }))
+ );
+ }
+}
+```
+
+### `stateChanges()`
+
+*What is it?* - Returns an Observable of the most recent changes as a `DocumentChangeAction[]`.
+
+*Why would you use it?* - The above methods return a synchronized array sorted in query order. `stateChanges()` emits changes as they occur rather than syncing the query order. This works well for ngrx integrations as you can build your own data structure in your reducer methods.
+
+*When would you not use it?* - When you just need a list of data. This is a more advanced usage of AngularFirestore.
+
+#### Example
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFirestore, AngularFirestoreCollection } from '@angular/fire/firestore';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+export interface AccountDeposit { description: string; amount: number; }
+export interface AccountDepositId extends AccountDeposit { id: string; }
+
+@Component({
+ selector: 'app-root',
+ template: `{%raw%}
+
+
+ {{ deposit.description }} for {{ deposit.amount }}
+
+
+ {%endraw%}`
+})
+export class AppComponent {
+ private depositCollection: AngularFirestoreCollection;
+ deposits: Observable;
+ constructor(private readonly afs: AngularFirestore) {
+ this.depositCollection = afs.collection('deposits');
+ this.deposits = this.depositCollection.stateChanges(['added']).pipe(
+ map(actions => actions.map(a => {
+ const data = a.payload.doc.data() as AccountDeposit;
+ const id = a.payload.doc.id;
+ return { id, ...data };
+ }))
+ );
+ }
+}
+```
+
+### `auditTrail()`
+
+*What is it?* - Returns an Observable of `DocumentChangeAction[]` as they occur. Similar to `stateChanges()`, but instead it keeps around the trail of events as an array.
+
+*Why would you use it?* - This method is like `stateChanges()` except it is not ephemeral. It collects each change in an array as they occur. This is useful for ngrx integrations where you need to replay the entire state of an application. This also works as a great debugging tool for all applications. You can simply write `afs.collection('items').auditTrail().subscribe(console.log)` and check the events in the console as they occur.
+
+*When would you not use it?* - When you just need a list of data. This is a more advanced usage of AngularFirestore.
+
+#### Example
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFirestore, AngularFirestoreCollection } from '@angular/fire/firestore';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+export interface AccountLogItem { description: string; amount: number; }
+export interface AccountLogItemId extends AccountLogItem { id: string; }
+
+@Component({
+ selector: 'app-root',
+ template: `{%raw%}
+
+
+ {{ log.description }} for {{ log.amount }}
+
+
+ {%endraw%}`
+})
+export class AppComponent {
+ private accountLogCollection: AngularFirestoreCollection;
+ accountLogs: Observable;
+ constructor(private readonly afs: AngularFirestore) {
+ this.accountLogCollection = afs.collection('accountLog');
+ this.accountLogs = this.accountLogCollection.auditTrail().pipe(
+ map(actions => actions.map(a => {
+ const data = a.payload.doc.data() as AccountLogItem;
+ const id = a.payload.doc.id;
+ return { id, ...data };
+ }))
+ );
+ }
+}
+```
+
+### Limiting events
+
+There are three `DocumentChangeType`s in Firestore: `added`, `removed`, and `modified`. Each streaming method listens to all three by default. However, you may only be intrested in one of these events. You can specify which events you'd like to use through the first parameter of each method:
+
+#### Basic example
+
+```ts
+constructor(private afs: AngularFirestore): {
+ this.itemsCollection = afs.collection('items');
+ this.items = this.itemsCollection.snapshotChanges(['added', 'removed']);
+}
+```
+
+#### Component example
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFirestore, AngularFirestoreCollection } from '@angular/fire/firestore';
+import { Observable } from 'rxjs';
+
+@Component({
+ selector: 'app-root',
+ template: `{%raw%}
+
+
+ {{ item.name }}
+
+
+ {%endraw%}`
+})
+export class AppComponent {
+ private itemsCollection: AngularFirestoreCollection;
+ items: Observable;
+ constructor(private afs: AngularFirestore) {
+ this.itemsCollection = afs.collection('items');
+ this.items = this.itemsCollection.valueChanges(['added', 'removed']);
+ }
+}
+```
+
+## State based vs. action based
+
+Each one of these methods falls into two categories: state based and action based. State based methods return the state of your collection "as-is". Whereas action based methods return "what happened" in your collection.
+
+For example, a user updates the third item in a list. In a state based method like `.valueChanges()` will update the third item in the collection and return an array of JSON data. This is how your state looks.
+
+## Adding documents to a collection
+
+To add a new document to a collection with a generated id use the `add()` method. This method uses the type provided by the generic class to validate it's type structure.
+
+#### Basic example
+
+```ts
+constructor(private afs: AngularFirestore): {
+ const shirtsCollection = afs.collection('tshirts');
+ shirtsCollection.add({ name: 'item', price: 10 });
+}
+```
+
+## Manipulating individual documents
+
+To retrieve, update, or delete an individual document you can use the `doc()` method. This method returns an `AngularFirestoreDocument`, which provides methods for streaming, updating, and deleting. [See Using Documents with AngularFirestore for more information on how to use documents](/firestore/documents).
+
diff --git a/site/src/firestore/documents.md b/site/src/firestore/documents.md
new file mode 100644
index 000000000..6639b3eeb
--- /dev/null
+++ b/site/src/firestore/documents.md
@@ -0,0 +1,120 @@
+---
+title: Documents
+eleventyNavigation:
+ key: Documents
+ parent: Firestore
+---
+
+## Documents in AngularFirestore
+
+Cloud Firestore is a NoSQL, document-oriented database. Unlike a SQL database, there are no tables or rows. Instead, you store data in *documents*, which are organized into *collections*.
+Each *document* contains a set of key-value pairs. Cloud Firestore is optimized for storing large collections of small documents.
+
+## Using `AngularFirestoreDocument`
+
+The `AngularFirestoreDocument` service is a wrapper around the native Firestore SDK's [`DocumentReference` type](https://firebase.google.com/docs/reference/js/firebase.firestore.DocumentReference). It is a generic service that provides you with a strongly typed set of methods for manipulating and streaming data. This service is designed for use as an `@Injectable()`.
+
+
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFirestore, AngularFirestoreDocument } from '@angular/fire/firestore';
+import { Observable } from 'rxjs';
+
+export interface Item { name: string; }
+
+@Component({
+ selector: 'app-root',
+ template: `{% raw %}
+
+ {{ (item | async)?.name }}
+
+ {% endraw %}`
+})
+export class AppComponent {
+ private itemDoc: AngularFirestoreDocument;
+ item: Observable;
+ constructor(private afs: AngularFirestore) {
+ this.itemDoc = afs.doc('items/1');
+ this.item = this.itemDoc.valueChanges();
+ }
+ update(item: Item) {
+ this.itemDoc.update(item);
+ }
+}
+```
+
+## The `DocumentChangeAction` type
+
+With the exception of the `valueChanges()`, each streaming method returns an Observable of `DocumentChangeAction[]`.
+
+A `DocumentChangeAction` gives you the `type` and `payload` properties. The `type` tells when what `DocumentChangeType` operation occured (`added`, `modified`, `removed`). The `payload` property is a `DocumentChange` which provides you important metadata about the change and a `doc` property which is the `DocumentSnapshot`.
+
+```ts
+interface DocumentChangeAction {
+ //'added' | 'modified' | 'removed';
+ type: DocumentChangeType;
+ payload: DocumentChange;
+}
+
+interface DocumentChange {
+ type: DocumentChangeType;
+ doc: DocumentSnapshot;
+ oldIndex: number;
+ newIndex: number;
+}
+
+interface DocumentSnapshot {
+ exists: boolean;
+ ref: DocumentReference;
+ id: string;
+ metadata: SnapshotMetadata;
+ data(): DocumentData;
+ get(fieldPath: string): any;
+}
+```
+
+## Streaming document data
+
+There are multiple ways of streaming collection data from Firestore.
+
+## `valueChanges({ idField?: string })`
+
+*What is it?* - Returns an Observable of document data. All Snapshot metadata is stripped. This method provides only the data. Optionally, you can pass an options object with an `idField` key containing a string. If provided, the returned object will include its document ID mapped to a property with the name provided by `idField`.
+
+*Why would you use it?* - When you just need the object data. No document metadata is attached which makes it simple to render to a view.
+
+*When would you not use it?* - When you need document metadata.
+
+## `snapshotChanges()`
+
+*What is it?* - Returns an Observable of data as a `DocumentChangeAction`.
+
+*Why would you use it?* - When you need the document data but also want to keep around metadata. This metadata provides you the underyling `DocumentReference` and document id. Having the document's id around makes it easier to use data manipulation methods. This method gives you more horsepower with other Angular integrations such as ngrx, forms, and animations due to the `type` property. The `type` property on each `DocumentChangeAction` is useful for ngrx reducers, form states, and animation states.
+
+*When would you not use it?* - When you simply need to render data to a view and don't want to do any extra processing.
+
+## Manipulating documents
+
+AngularFirestore provides methods for setting, updating, and deleting document data.
+
+- `set(data: T)` - Destructively updates a document's data.
+- `update(data: T)` - Non-destructively updates a document's data.
+- `delete()` - Deletes an entire document. Does not delete any nested collections.
+
+## Querying?
+
+Querying has no effect on documents. Documents are a single object and querying effects a range of multiple documents. If you are looking for querying then you want to use a collection.
+
+## Retrieving nested collections
+
+Nesting collections is a great way to structure your data. This allows you to group related data structures together. If you are creating a "Task List" site, you can group "tasks" under a user: `user//tasks`.
+
+To retrieve a nested collection use the `collection(path: string)` method.
+
+```ts
+constructor(private afs: AngularFirestore) {
+ this.userDoc = afs.doc('user/david');
+ this.tasks = this.userDoc.collection('tasks').valueChanges();
+}
+```
diff --git a/site/src/firestore/firestore.11tydata.json b/site/src/firestore/firestore.11tydata.json
new file mode 100644
index 000000000..b924da1f3
--- /dev/null
+++ b/site/src/firestore/firestore.11tydata.json
@@ -0,0 +1,4 @@
+{
+ "layout": "guide.njk",
+ "tags": "guides"
+}
diff --git a/site/src/firestore/index.md b/site/src/firestore/index.md
new file mode 100644
index 000000000..647cfb988
--- /dev/null
+++ b/site/src/firestore/index.md
@@ -0,0 +1,5 @@
+---
+eleventyNavigation:
+ key: Firestore
+ order: 2
+---
\ No newline at end of file
diff --git a/site/src/functions/functions.11tydata.json b/site/src/functions/functions.11tydata.json
new file mode 100644
index 000000000..b924da1f3
--- /dev/null
+++ b/site/src/functions/functions.11tydata.json
@@ -0,0 +1,4 @@
+{
+ "layout": "guide.njk",
+ "tags": "guides"
+}
diff --git a/site/src/functions/getting-started.md b/site/src/functions/getting-started.md
new file mode 100644
index 000000000..2ddf6f632
--- /dev/null
+++ b/site/src/functions/getting-started.md
@@ -0,0 +1,170 @@
+---
+title: Getting started
+eleventyNavigation:
+ key: Getting started
+ parent: Functions
+---
+
+## Using AngularFireFunctions
+
+The Cloud Functions for Firebase client SDKs let you call functions directly from a Firebase app. To call a function from your app in this way, write and deploy an HTTPS Callable function in Cloud Functions, and then add client logic to call the function from your app.
+
+## Import the `NgModule`
+
+Cloud Functions for AngularFire is contained in the `@angular/fire/functions` module namespace. Import the `AngularFireFunctionsModule` in your `NgModule`. This sets up the `AngularFireFunction` service for dependency injection.
+
+```ts
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+import { AppComponent } from './app.component';
+import { AngularFireModule } from '@angular/fire';
+import { AngularFireFunctionsModule } from '@angular/fire/functions';
+import { environment } from '../environments/environment';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ AngularFireModule.initializeApp(environment.firebase),
+ AngularFireFunctionsModule
+ ],
+ declarations: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule {}
+```
+
+## Injecting the `AngularFireFunctions` service
+
+Once the `AngularFireFunctionsModule` is registered you can inject the `AngularFireFunctions` service.
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFireFunctions } from '@angular/fire/functions';
+
+@Component({
+ selector: 'app-component',
+ template: ``
+})
+export class AppComponent {
+ constructor(private fns: AngularFireFunctions) { }
+}
+```
+
+## Creating a callable function
+
+AngularFireFunctions is super easy. You create a function on the server side and then "call" it by its name with the client library.
+
+| method | |
+| ---------|--------------------|
+| `httpCallable(name: string): (data: T) ` | Creates a callable function based on a function name. Returns a function that can create the observable of the http call. |
+```ts
+
+import { Component } from '@angular/core';
+import { AngularFireFunctions } from '@angular/fire/functions';
+
+@Component({
+ selector: 'app-root',
+ template: `{ data$ | async }`
+})
+export class AppComponent {
+ constructor(private fns: AngularFireFunctions) {
+ const callable = fns.httpsCallable('my-fn-name');
+ this.data$ = callable({ name: 'some-data' });
+ }
+}
+```
+
+Notice that calling `httpsCallable()` does not initiate the request. It creates a function, which when called creates an Observable, subscribe or convert it to a Promise to initiate the request.
+
+## Configuration via Dependency Injection
+
+### Functions Region
+
+Allow configuration of the Function's region by adding `REGION` to the `providers` section of your `NgModule`. The default is `us-central1`.
+
+```ts
+import { NgModule } from '@angular/core';
+import { AngularFireFunctionsModule, REGION } from '@angular/fire/functions';
+
+@NgModule({
+ imports: [
+ ...
+ AngularFireFunctionsModule,
+ ...
+ ],
+ ...
+ providers: [
+ { provide: REGION, useValue: 'asia-northeast1' }
+ ]
+})
+export class AppModule {}
+
+```
+
+### Cloud Functions emulator
+
+Point callable Functions to the Cloud Function emulator by adding `USE_EMULATOR` to the `providers` section of your `NgModule`.
+
+```ts
+import { NgModule } from '@angular/core';
+import { AngularFireFunctionsModule, USE_EMULATOR } from '@angular/fire/functions';
+
+@NgModule({
+ imports: [
+ ...
+ AngularFireFunctionsModule,
+ ...
+ ],
+ ...
+ providers: [
+ { provide: USE_EMULATOR, useValue: ['localhost', 5001] }
+ ]
+})
+export class AppModule {}
+
+```
+
+[Learn more about integration with the Firebase Emulator suite on our dedicated guide here](../emulators/emulators.md).
+
+## Firebase Hosting integration
+
+If you serve your app using [Firebase Hosting](https://firebase.google.com/docs/hosting/), you can configure Functions to be served from the same domain as your app. This will avoid an extra round-trip per function call due to [CORS preflight request](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request). This only applies to sites hosted via firebase on `us-central1`.
+
+To set this up, you first need to update your `hosting` section in `firebase.json` and add one `rewrite` rule per function:
+
+```json
+ "hosting": {
+ "rewrites": [
+ {
+ "source": "/someFunction",
+ "function": "someFunction"
+ },
+ {
+ "source": "/anotherFunction",
+ "function": "anotherFunction"
+ },
+ ...
+ ]
+ }
+```
+
+Deploy your hosting project to the new settings go into effect, finally configure functions origin to point at your app domain:
+
+```ts
+import { NgModule } from '@angular/core';
+import { AngularFireFunctionsModule, ORIGIN, NEW_ORIGIN_BEHAVIOR } from '@angular/fire/functions';
+
+@NgModule({
+ imports: [
+ ...
+ AngularFireFunctionsModule,
+ ...
+ ],
+ ...
+ providers: [
+ { provide: NEW_ORIGIN_BEHAVIOR, useValue: true },
+ { provide: ORIGIN, useValue: 'https://project-name.web.app/' }
+ ]
+})
+export class AppModule {}
+```
diff --git a/site/src/functions/index.md b/site/src/functions/index.md
new file mode 100644
index 000000000..0cc6ccdac
--- /dev/null
+++ b/site/src/functions/index.md
@@ -0,0 +1,5 @@
+---
+eleventyNavigation:
+ key: Functions
+ order: 7
+---
diff --git a/site/src/get-started/deploying.md b/site/src/get-started/deploying.md
new file mode 100644
index 000000000..b0e9011b4
--- /dev/null
+++ b/site/src/get-started/deploying.md
@@ -0,0 +1,150 @@
+---
+title: Deploying
+eleventyNavigation:
+ key: Deploying
+ parent: Get started
+---
+
+## Static or Server-side rendered
+
+In this guide, we'll look at how to use `@angular/fire` to automatically deploy an Angular application to Firebase Hosting or Cloud Functions by using the Angular CLI.
+
+`@angular/fire` uses Firebase functions to deploy your Angular Universal projects, with server-side rendering enabled.
+
+**Angular Universal deployments work with `@nguniversal/*` version 9.0.0 and above**.
+
+## Add `@angular/fire` to your project
+
+First, you need to add the `@angular/fire` package to your project. In your Angular CLI project run:
+
+```shell
+ng add @angular/fire
+```
+
+*Note that the command above assumes you have global Angular CLI installed. To install Angular CLI globally run `npm i -g @angular/cli`.*
+
+First, the command above will check if you have an Angular universal project. It'll do so by looking at your `angular.json` project, looking for a `server` target for the specified project. If it finds one, it'll ask you if you want to deploy the project in a firebase function.
+
+After that it will trigger the `@angular/fire` `ng-add` schematics. The schematics will open a web browser and guide you through the Firebase authentication flow (if you're not signed in already). After you authenticate, you'll see a prompt to select a Firebase hosting project.
+
+The schematics will do the following:
+
+1. Add `@angular/fire` to your list of dependencies
+2. Create `firebase.json`, `.firebaserc` files in the root of your workspace. You can use them to configure your firebase hosting deployment. Find more about them [here](https://firebase.google.com/docs/hosting/full-config)
+3. Update your workspace file (`angular.json`) by inserting the `deploy` builder
+
+In the end, your `angular.json` project will look like below:
+
+```json5
+{
+ "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+ "version": 1,
+ "newProjectRoot": "projects",
+ "projects": {
+ "sample-app": {
+ // ...
+ "deploy": {
+ "builder": "@angular/fire:deploy",
+ "options": {} // Here you may find an "ssr": true option if you've
+ // selected that you want to deploy your Angular universal project
+ // as a firebase function.
+ }
+ }
+ },
+ // ...
+ "defaultProject": "sample-app"
+
+}
+```
+
+If you want to add deployment capabilities to a different project in your workspace, you can run:
+
+```bash
+ng add @angular/fire --project=[PROJECT_NAME]
+```
+
+## Deploying the project
+
+As the second step, to deploy your project run:
+
+```bash
+ng deploy --project=[PROJECT_NAME]
+```
+
+*The `--project` option is optional. Learn more [here](https://angular.io/cli/deploy).*
+
+The command above will trigger:
+
+1. Production build of your application
+2. Deployment of the produced assets to the firebase hosting project you selected during `ng add`
+
+If you've specified that you want a server-side rendering enabled deployment in a firebase function, the command will also:
+
+1. Create a firebase function in `dist`, which directly consumes `main.js` from your server output directory.
+2. Create `package.json` for the firebase function with the required dependencies.
+3. Deploy the static assets to firebase hosting and your universal server as a Firebase function.
+
+If you want to preview your Angular Universal project before we deploy it as a Firebase Function you can run:
+
+```
+ng deploy --preview
+```
+
+We'll create the function and a `package.json` in your project output directory. This way, you can later run `firebase serve` in your project root so you can test everything before deploying.
+
+## Customization
+
+To customize the deployment flow, you can use the configuration files you're already familiar with from `firebase-tools`. You can find more in the [firebase documentation](https://firebase.google.com/docs/hosting/full-config).
+
+### Configuring Cloud Functions
+
+Setting `functionsNodeVersion` and `functionsRuntimeOptions` in your `angular.json` allow you to custimze the version of Node.js Cloud Functions is running and run-time settings like timeout, VPC connectors, and memory.
+
+```json
+"deploy": {
+ "builder": "@angular/fire:deploy",
+ "options": {
+ "functionsNodeVersion": 12,
+ "functionsRuntimeOptions": {
+ "memory": "2GB",
+ "timeoutSeconds": 10,
+ "vpcConnector": "my-vpc-connector",
+ "vpcConnectorEgressSettings": "PRIVATE_RANGES_ONLY"
+ }
+ }
+}
+```
+
+### Working with multiple Firebase Projects
+
+If you have multiple build targets and deploy targets, it is possible to specify them in your `angular.json` or `workspace.json`.
+
+It is possible to use either your project name or project alias in `firebaseProject`. The setting provided here is equivalent to passing a project name or alias to `firebase deploy --project projectNameOrAlias`.
+
+The `buildTarget` simply points to an existing build configuration for your project. Most projects have a default configuration and a production configuration (commonly activated by using the `--prod` flag) but it is possible to specify as many build configurations as needed.
+
+You may specify a `buildTarget` and `firebaseProject` in your `options` as follows:
+
+```json
+"deploy": {
+ "builder": "@angular/fire:deploy",
+ "options": {
+ "buildTarget": "projectName:build",
+ "firebaseProject": "developmentProject"
+ },
+ "configurations": {
+ "production": {
+ "buildTarget": "projectName:build:production",
+ "firebaseProject": "productionProject"
+ }
+ }
+}
+```
+
+The above configuration specifies the following:
+
+1. `ng deploy` will deploy the default project with default configuration.
+2. `ng deploy projectName` will deploy the specified project with default configuration.
+3. `ng deploy projectName --prod` or `ng deploy projectName --configuration='production'` will deploy `projectName` with production build settings to your production environment.
+
+All of the options are optional. If you do not specify a `buildTarget`, it defaults to a production build (`projectName:build:production`). If you do not specify a `firebaseProject`, it defaults to the first matching deploy target found in your `.firebaserc` (where your projectName is the same as your Firebase deploy target name). The `configurations` section is also optional.
diff --git a/site/src/get-started/get-started.11tydata.json b/site/src/get-started/get-started.11tydata.json
new file mode 100644
index 000000000..b924da1f3
--- /dev/null
+++ b/site/src/get-started/get-started.11tydata.json
@@ -0,0 +1,4 @@
+{
+ "layout": "guide.njk",
+ "tags": "guides"
+}
diff --git a/site/src/get-started/index.md b/site/src/get-started/index.md
new file mode 100644
index 000000000..0485183af
--- /dev/null
+++ b/site/src/get-started/index.md
@@ -0,0 +1,5 @@
+---
+eleventyNavigation:
+ key: Get started
+ order: 1
+---
diff --git a/site/src/get-started/local-development.md b/site/src/get-started/local-development.md
new file mode 100644
index 000000000..b9d986a81
--- /dev/null
+++ b/site/src/get-started/local-development.md
@@ -0,0 +1,135 @@
+---
+title: Local development
+eleventyNavigation:
+ key: Local development
+ parent: Get started
+---
+
+## Connect to the Firebase Emulator Suite
+
+In this guide, we'll look at how to use `@angular/fire` to connect an Angular application with the Firebase Emulator Suite to start prototyping your apps.
+
+There are four supported emulators, all of them available at the Firebase suite workflow:
+
+- [Authentication Emulator](https://firebase.google.com/docs/emulator-suite/connect_auth)
+- [Realtime Database Emulator](https://firebase.google.com/docs/emulator-suite/connect_rtdb)
+- [Cloud Firestore Emulator](https://firebase.google.com/docs/emulator-suite/connect_firestore)
+- [Cloud Functions Emulator](https://firebase.google.com/docs/emulator-suite/connect_functions)
+
+*The Auth Emulator only works with Firebase v8 and above, which is supported by `@angular/fire` 6.1.0 or higher*.
+
+Before configuring these emulators at the Angular App, be sure to install the ones you need by following the [Install, configure and integrate Local Emulator Suite](https://firebase.google.com/docs/emulator-suite/install_and_configure) documentation.
+
+Initialize firebase to your project:
+
+```shell
+firebase init
+```
+
+Then launch the emulator setup wizard:
+
+```shell
+firebase init emulators
+```
+
+Follow the instructions to download whatever emulator you want to use then checkout that the `firebase.json` file got updated with the default ports per emulator, something like this:
+
+```json
+{
+ // Existing firebase configuration ...
+ // Optional emulator configuration. Default
+ // values are used if absent.
+ "emulators": {
+ "firestore": {
+ "port": "8080"
+ },
+ "ui": {
+ "enabled": true, // Default is `true`
+ "port": 4000 // If unspecified, see CLI log for selected port
+ },
+ "auth": {
+ "port": "9099"
+ },
+ "functions": {
+ "port": "5001"
+ },
+ "database": {
+ "port": "9000"
+ },
+ "pubsub": {
+ "port": "8085"
+ }
+ }
+}
+```
+
+## Import the DI Tokens at your AppModule
+
+Configuring your app to connect to local emulators is easily done by using dependency injection tokens provided by the library. However, there are slighty changes between 6.0.0 and 6.1.0 in the way it was done.
+
+Each module (database, firestore, auth, function) provides `USE_EMULATOR` token to configure the emulator `host` and `port` by passing a tuple of `[string, number]` values, which are set by default to `localhost` and the asigned port from your `firebase.json` file.
+
+Import these tokens at your `app.module.ts` as follow:
+
+```ts
+import { USE_EMULATOR as USE_AUTH_EMULATOR } from '@angular/fire/auth';
+import { USE_EMULATOR as USE_DATABASE_EMULATOR } from '@angular/fire/database';
+import { USE_EMULATOR as USE_FIRESTORE_EMULATOR } from '@angular/fire/firestore';
+import { USE_EMULATOR as USE_FUNCTIONS_EMULATOR } from '@angular/fire/functions';
+
+@NgModule({
+ // ... Existing configuration
+ providers: [
+ // ... Existing Providers
+ { provide: USE_AUTH_EMULATOR, useValue: environment.useEmulators ? ['localhost', 9099] : undefined },
+ { provide: USE_DATABASE_EMULATOR, useValue: environment.useEmulators ? ['localhost', 9000] : undefined },
+ { provide: USE_FIRESTORE_EMULATOR, useValue: environment.useEmulators ? ['localhost', 8080] : undefined },
+ { provide: USE_FUNCTIONS_EMULATOR, useValue: environment.useEmulators ? ['localhost', 5001] : undefined },
+ ]
+})
+export class AppModule { }
+```
+
+The environment `useEmulators` flag is used to control whenever the app should connect to the emulators, which is usually done in non-production environments.
+
+Also you can opt-in the new way of setting the Cloud Functions [origin](https://firebase.google.com/docs/functions/locations) in Firebase v8 by using the `NEW_ORIGIN_BEHAVIOR` token in conjuction with the already present `ORIGIN` token.
+
+```ts
+import { isDevMode, NgModule } from '@angular/core';
+import { ORIGIN as FUNCTIONS_ORIGIN, NEW_ORIGIN_BEHAVIOR } from '@angular/fire/functions';
+
+@NgModule({
+ // ... Existing configuration
+ providers: [
+ // ... Existing Providers
+ { provide: NEW_ORIGIN_BEHAVIOR, useValue: true },
+ { provide: FUNCTIONS_ORIGIN, useFactory: () => isDevMode() ? undefined : location.origin },
+ ]
+})
+export class AppModule { }
+```
+
+## Older method (6.0.0)
+
+With the exception of the Auth Emulator, the old way of setting the `host` and `port` for each emulator was done using a different set of tokens by passing the entire url path as string.
+
+```ts
+import { URL as DATABASE_URL } from '@angular/fire/database';
+import { ORIGIN as FUNCTIONS_ORIGIN } from '@angular/fire/functions';
+import { SETTINGS as FIRESTORE_SETTINGS } from '@angular/fire/firestore';
+
+@NgModule({
+ // ... Existing configuration
+ providers: [
+ {
+ provide: DATABASE_URL,
+ useValue: environment.useEmulators ? `http://localhost:9000?ns=${environment.firebase.projectId}` : undefined
+ },
+ { provide: FIRESTORE_SETTINGS, useValue: environment.useEmulators ? { host: 'localhost:8080', ssl: false } : {} },
+ { provide: FUNCTIONS_ORIGIN, useFactory: environment.useEmulators ? 'http://localhost:5001/' : undefined },
+ ]
+})
+export class AppModule { }
+```
+
+For older versions, please upgrade your app to latest version to get the advantages of these new features :rocket:
diff --git a/site/src/get-started/quick-start.md b/site/src/get-started/quick-start.md
new file mode 100644
index 000000000..a4f91d1ff
--- /dev/null
+++ b/site/src/get-started/quick-start.md
@@ -0,0 +1,166 @@
+---
+title: Quick start
+eleventyNavigation:
+ key: Quick start
+ parent: Get started
+---
+
+## Create a new project
+
+```bash
+npm install -g @angular/cli
+ng new
+cd
+```
+
+The Angular CLI's `new` command will set up the latest Angular build in a new project structure.
+
+## Install AngularFire and Firebase
+
+```bash
+ng add @angular/fire
+```
+
+Now that you have a new project setup, install AngularFire and Firebase from npm.
+
+## Add Firebase config to environments variable
+
+Open `/src/environments/environment.ts` and add your Firebase configuration. You can find your project configuration in [the Firebase Console](https://console.firebase.google.com). Click the Gear icon next to Project Overview, in the Your Apps section, create a new app and choose the type Web. Give the app a name and copy the config values provided.
+
+```ts
+export const environment = {
+ production: false,
+ firebase: {
+ apiKey: '',
+ authDomain: '',
+ databaseURL: '',
+ projectId: '',
+ storageBucket: '',
+ messagingSenderId: '',
+ appId: '',
+ measurementId: ''
+ }
+};
+```
+
+## Setup `@NgModule` for the `AngularFireModule`
+
+Open `/src/app/app.module.ts`, inject the Firebase providers, and specify your Firebase configuration.
+
+```ts
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+import { AppComponent } from './app.component';
+import { AngularFireModule } from '@angular/fire';
+import { environment } from '../environments/environment';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ AngularFireModule.initializeApp(environment.firebase)
+ ],
+ declarations: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule {}
+```
+
+## Setup individual `@NgModule`s
+
+After adding the AngularFireModule you also need to add modules for the individual @NgModules that your application needs.
+
+For example if your application was using both Google Analytics and the Firestore you would add `AngularFireAnalyticsModule` and `AngularFirestoreModule`:
+
+```ts
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+import { AppComponent } from './app.component';
+import { AngularFireModule } from '@angular/fire';
+import { AngularFireAnalyticsModule } from '@angular/fire/analytics';
+import { AngularFirestoreModule } from '@angular/fire/firestore';
+import { environment } from '../environments/environment';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ AngularFireModule.initializeApp(environment.firebase),
+ AngularFireAnalyticsModule,
+ AngularFirestoreModule
+ ],
+ declarations: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule {}
+```
+
+## Inject `AngularFirestore`
+
+Open `/src/app/app.component.ts`, and make sure to modify/delete any tests to get the sample working (tests are still important, you know):
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFirestore } from '@angular/fire/firestore';
+
+@Component({
+ selector: 'app-root',
+ templateUrl: 'app.component.html',
+ styleUrls: ['app.component.css']
+})
+export class AppComponent {
+ constructor(firestore: AngularFirestore) {
+
+ }
+}
+```
+
+## Bind a Firestore collection to a list
+
+In `/src/app/app.component.ts`:
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFirestore } from '@angular/fire/firestore';
+import { Observable } from 'rxjs';
+
+@Component({
+ selector: 'app-root',
+ templateUrl: 'app.component.html',
+ styleUrls: ['app.component.css']
+})
+export class AppComponent {
+ items: Observable;
+ constructor(firestore: AngularFirestore) {
+ this.items = firestore.collection('items').valueChanges();
+ }
+}
+```
+
+Open `/src/app/app.component.html`:
+
+```html
+
+
+ {{item.name}}
+
+
+```
+
+## Run your app locally
+
+```bash
+ng serve
+```
+
+Your Angular app will compile and serve locally, visit it we should see an empty list.
+
+In another tab [start adding data to an `items` collection in Firestore](https://firebase.google.com/docs/firestore/manage-data/add-data). *As we're not authenticating users yet, be sure to start Firestore in **test mode** or allow reading from the `items` collection in Security Rules (`allow read: if true`).*
+
+Once you've created a `items` collection and are inserting documents, you should see data streaming into your Angular application.
+
+## Deploy your app
+
+Finally, we can deploy the application to Firebase hosting:
+
+```bash
+ng deploy
+```
\ No newline at end of file
diff --git a/site/src/index.md b/site/src/index.md
new file mode 100644
index 000000000..f75605062
--- /dev/null
+++ b/site/src/index.md
@@ -0,0 +1,50 @@
+---
+layout: default.njk
+---
+
+{% headingone %}AngularFire{% endheadingone %}
+
+{% subheading %}The official library for Angular and Firebase{% endsubheading %}
+
+
+
+{% disclaimerprod %}
+
+## What is AngularFire?
+
+AngularFire smooths over the rough edges an Angular developer might encounter when implementing the framework-agnostic Firebase JS SDK & aims to provide a more natural developer experience by conforming to Angular conventions.
+
+### Dependency injection
+Provide and Inject Firebase services in your components
+
+### Zone.js wrappers
+Stable zones allow proper functionality of service workers, forms, SSR, and pre-rendering
+
+### Observable based
+Utilize RxJS rather than callbacks for realtime streams
+
+### NgRx friendly API
+Integrate with NgRx using AngularFire's action based APIs.
+
+### Lazy-loading
+AngularFire dynamically imports much of Firebase, reducing time to load your app
+
+### Deploy schematics
+Get your Angular application deployed on Firebase Hosting with a single command
+
+### Google Analytics
+Zero-effort Angular Router awareness in Google Analytics
+
+### Router Guards
+Guard your Angular routes with built-in Firebase Authentication checks
diff --git a/site/src/ionic/authentication.md b/site/src/ionic/authentication.md
new file mode 100644
index 000000000..a42a946e4
--- /dev/null
+++ b/site/src/ionic/authentication.md
@@ -0,0 +1,100 @@
+---
+title: Authentication
+eleventyNavigation:
+ key: Authentication
+ parent: Ionic
+---
+
+## Setting up Ionic and Firebase Auth
+
+First start out by installing the needed plugins below.
+
+```
+ionic cordova plugin add cordova-universal-links-plugin
+ionic cordova plugin add cordova-plugin-buildinfo
+ionic cordova plugin add cordova-plugin-browsertab
+ionic cordova plugin add cordova-plugin-inappbrowser
+(for ios)
+ionic cordova plugin add cordova-plugin-customurlscheme
+```
+
+## Add Firebase to your Ionic app
+
+Follow [this tutorial](https://github.com/angular/angularfire2/blob/master/docs/install-and-setup.md) to make a basic setup for your Ionic project.
+
+### To set up in an Android app
+
+Go to [Firebase console](https://console.firebase.google.com/) then click *Add Firebase to your Android app* and follow the setup steps.
+
+## Set up Firebase Authentication for Cordova
+
+*This is a summary from the [Firebase instructions](https://firebase.google.com/docs/auth/web/cordova).*
+
+### Setup Dynamic Link
+In the Firebase console, open the *Dynamic Links* section at bottom left panel, setup by their instruction
+
+Add this to config.xml at root level of project:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+```
+Make sure your `` the same with Android app's id you added in Firebase.
+
+## Add login code
+
+In `login.service.ts` add this function:
+
+```ts
+
+import { AngularFireAuth } from '@angular/fire/auth';
+import firebase from 'firebase/app';
+import AuthProvider = firebase.auth.AuthProvider;
+
+export class AuthService {
+ private user: firebase.User;
+ constructor(public afAuth: AngularFireAuth) {
+ afAuth.authState.subscribe(user => {
+ this.user = user;
+ });
+ }
+
+ signInWithFacebook() {
+ console.log('Sign in with Facebook');
+ return this.oauthSignIn(new firebase.auth.FacebookAuthProvider());
+ }
+
+ signInWithGoogle() {
+ console.log('Sign in with Google');
+ return this.oauthSignIn(new firebase.auth.GoogleAuthProvider());
+ }
+
+ private oauthSignIn(provider: AuthProvider) {
+ if (!(window).cordova) {
+ return this.afAuth.auth.signInWithPopup(provider);
+ } else {
+ return this.afAuth.auth.signInWithRedirect(provider)
+ .then(() => {
+ return this.afAuth.auth.getRedirectResult().then( result => {
+ // This gives you an Access Token. You can use it to access the associated APIs.
+ let token = result.credential.accessToken;
+ // The signed-in user info.
+ let user = result.user;
+ console.log(token, user);
+ }).catch(function(error) {
+ // Handle Errors here.
+ alert(error.message);
+ });
+ });
+ }
+ }
+}
+```
diff --git a/site/src/ionic/getting-started.md b/site/src/ionic/getting-started.md
new file mode 100644
index 000000000..a558072c7
--- /dev/null
+++ b/site/src/ionic/getting-started.md
@@ -0,0 +1,199 @@
+---
+title: Getting started
+eleventyNavigation:
+ key: Getting started
+ parent: Ionic
+---
+
+## Setup with Ionic CLI
+
+Before you start installing AngularFire, make sure you have latest version of Ionic cli installed. To verify run the command `ionic -v` and check your version. The CLI should be at least version 3.0.0 or greater.
+
+If not, you may need to do the following:
+
+```bash
+# if you have the wrong cli version only
+npm uninstall -g ionic
+npm cache clean
+
+# reinstall clean version
+npm install -g @ionic/cli
+```
+
+## Create a new project
+
+```bash
+ionic start
+cd
+```
+
+The Ionic CLI's `start` command will prompt you to pick a starting template, and scaffold out the project for you.
+
+## Test your Setup
+
+```bash
+ionic serve
+```
+
+Your default browser should start up and display a working Ionic app.
+
+## Install AngularFire & Firebase
+
+```bash
+npm install @angular/fire firebase --save
+```
+
+Now that you have a new project setup, install AngularFire and Firebase from npm.
+
+### Add Firebase config to environments variable
+
+Let's create a new file, `src/environment.ts` and start adding our Firebase config:
+
+```ts
+export const firebaseConfig = {
+ apiKey: '',
+ authDomain: '',
+ databaseURL: '',
+ projectId: '',
+ storageBucket: '',
+ messagingSenderId: ''
+};
+```
+
+
+## Add the `AngularFireModule`
+
+Open `/src/app/app.module.ts`, inject the Firebase providers, and specify your Firebase configuration. This can be found in your project at [the Firebase Console](https://console.firebase.google.com):
+
+```ts
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+import { IonicApp, IonicModule } from 'ionic-angular';
+import { MyApp } from './app.component';
+
+import { AngularFireModule } from '@angular/fire';
+import { firebaseConfig } from '../environment';
+
+@NgModule({
+ declarations: [ MyApp ],
+ imports: [
+ BrowserModule,
+ IonicModule.forRoot(MyApp),
+ AngularFireModule.initializeApp(firebaseConfig)
+ ],
+ bootstrap: [IonicApp],
+})
+export class AppModule {}
+
+```
+
+There will be more or less imports depending on your app. This is just an example setup.
+
+## Custom FirebaseApp Names
+You can optionally provide a custom FirebaseApp name with `initializeApp`.
+
+```ts
+@NgModule({
+ declarations: [ MyApp ],
+ imports: [
+ BrowserModule,
+ IonicModule.forRoot(MyApp),
+ AngularFireModule.initializeApp(firebaseConfig, 'my-app-name')
+ ],
+ bootstrap: [IonicApp],
+})
+export class AppModule {}
+```
+
+## Adding Feature Modules
+
+If your application was using both Firebase Auth and the Realtime Database you would add the following modules.
+
+```ts
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+import { IonicApp, IonicModule } from 'ionic-angular';
+import { MyApp } from './app.component';
+
+import { AngularFireModule } from '@angular/fire';
+import { firebaseConfig } from '../environment';
+import { AngularFireDatabaseModule } from '@angular/fire/database';
+import { AngularFireAuthModule } from '@angular/fire/auth';
+
+
+@NgModule({
+ declarations: [ MyApp ],
+ imports: [
+ BrowserModule,
+ // imports firebase/app needed for everything
+ AngularFireModule.initializeApp(firebaseConfig),
+ // imports firebase/database, only needed for database features
+ AngularFireDatabaseModule,
+ // imports firebase/auth, only needed for auth features
+ AngularFireAuthModule,
+ IonicModule.forRoot(MyApp),
+ ],
+ bootstrap: [IonicApp],
+})
+
+```
+
+### Inject AngularFireDatabase
+
+Open `/src/pages/home/home.ts`, and start to import `AngularFireDatabase`.
+
+```ts
+import { Component } from '@angular/core';
+import { NavController } from 'ionic-angular';
+import { AngularFireDatabase } from '@angular/fire/database';
+import { Observable } from 'rxjs/Observable';
+
+@Component({
+ selector: 'page-home',
+ templateUrl: 'home.html'
+})
+export class HomePage {
+ items: Observable;
+ constructor(
+ public db: AngularFireDatabase,
+ public navCtrl: NavController,
+ ) {}
+
+}
+```
+
+### 8. Bind to a list
+
+In `/src/pages/home/home.ts`:
+
+```ts
+import { Component } from '@angular/core';
+import { NavController } from 'ionic-angular';
+import { AngularFireDatabase } from '@angular/fire/database';
+import { Observable } from 'rxjs/Observable';
+
+@Component({
+ selector: 'page-home',
+ templateUrl: `{%raw%}
+
+ ---
+
+
+
+
+ {{item | json}}
+
+{%endraw%}`
+})
+export class HomePage {
+ items: Observable;
+ constructor(
+ public db: AngularFireDatabase,
+ public navCtrl: NavController,
+ ) {
+ this.items = db.list('list').valueChanges();
+ }
+
+}
+```
+
diff --git a/site/src/ionic/index.md b/site/src/ionic/index.md
new file mode 100644
index 000000000..3bc76eaaf
--- /dev/null
+++ b/site/src/ionic/index.md
@@ -0,0 +1,6 @@
+---
+eleventyNavigation:
+ key: Ionic
+ order: 12
+---
+
diff --git a/site/src/ionic/ionic.11tydata.json b/site/src/ionic/ionic.11tydata.json
new file mode 100644
index 000000000..b924da1f3
--- /dev/null
+++ b/site/src/ionic/ionic.11tydata.json
@@ -0,0 +1,4 @@
+{
+ "layout": "guide.njk",
+ "tags": "guides"
+}
diff --git a/site/src/js/click-card.js b/site/src/js/click-card.js
new file mode 100644
index 000000000..66a2587ac
--- /dev/null
+++ b/site/src/js/click-card.js
@@ -0,0 +1,17 @@
+customElements.define('eap-click-card', class extends HTMLElement {
+ connectedCallback() {
+ let down;
+ let up;
+ // Enhance to a pointer only if the JavaScript applies
+ this.style.cursor = 'pointer';
+ // Note: This only works for a single link or the first one.
+ const firstOrOnlyLink = this.querySelector('a');
+ this.onmousedown = () => down = +new Date();
+ this.onmouseup = () => {
+ up = +new Date();
+ if ((up - down) < 200) {
+ firstOrOnlyLink.click();
+ }
+ }
+ }
+});
\ No newline at end of file
diff --git a/site/src/js/menu-button.js b/site/src/js/menu-button.js
new file mode 100644
index 000000000..47018438f
--- /dev/null
+++ b/site/src/js/menu-button.js
@@ -0,0 +1,13 @@
+customElements.define('eap-menu-button', class extends HTMLElement {
+ connectedCallback() {
+ const menuId = this.getAttribute('data-menu-id');
+ const menuEl = document.getElementById(menuId);
+ const button = document.createElement('button');
+ button.classList.add('fixed', 'w-16', 'h-16', 'text-white', 'rounded-full', 'shadow-lg', 'bottom-6', 'right-6', 'bg-grey-700', 'focus:ring-grey-600' , 'z-50', 'focus:ring-4', 'md:hidden', 'lg:hidden', 'xl:hidden');
+ button.textContent = '🔥';
+ this.appendChild(button);
+ button.addEventListener('click', clickEvent => {
+ menuEl.classList.toggle('slideIn');
+ });
+ }
+});
diff --git a/site/src/js/tab-switcher.js b/site/src/js/tab-switcher.js
new file mode 100644
index 000000000..5c6a03a68
--- /dev/null
+++ b/site/src/js/tab-switcher.js
@@ -0,0 +1,25 @@
+customElements.define('eap-tab-switcher', class extends HTMLElement {});
+customElements.define('eap-tab-list', class extends HTMLElement {
+ connectedCallback() {
+ this.buttonTabs = this.querySelectorAll('button');
+ for(let button of this.buttonTabs) {
+ button.addEventListener('click', clickEvent => {
+ const activeButton = this.querySelector('button[aria-selected="true"]');
+ const activePanelId = activeButton.dataset.panel;
+ const panelToDisplayId = button.dataset.panel;
+ const panelToDisplay = document.querySelector(`#${panelToDisplayId}`);
+ const activePanel = document.querySelector(`#${activePanelId}`);
+ if(activeButton.id !== button.id) {
+ button.setAttribute('aria-selected', true);
+ activeButton.setAttribute('aria-selected', false);
+ panelToDisplay.classList.add('block');
+ panelToDisplay.classList.remove('hidden');
+ activePanel.classList.remove('block');
+ activePanel.classList.add('hidden');
+ }
+ });
+ }
+ }
+});
+customElements.define('eap-tab-panel-list', class extends HTMLElement {});
+customElements.define('eap-tab-panel', class extends HTMLElement { });
\ No newline at end of file
diff --git a/site/src/messaging/getting-started.md b/site/src/messaging/getting-started.md
new file mode 100644
index 000000000..b69a5ca80
--- /dev/null
+++ b/site/src/messaging/getting-started.md
@@ -0,0 +1,234 @@
+---
+title: Getting started
+eleventyNavigation:
+ key: Getting started
+ parent: Messaging
+---
+
+## Using AngularFireMessaging
+
+The FCM JavaScript API lets you receive notification messages in web apps running in browsers that support the Push API.
+
+## Not readily compatible with the Angular Service Worker
+
+If you are using the Angular Service Worker, you are not currently able to use AngularFireMessaging out-of-the-box. If you'd like this feature please add your 👍 to [this issue](https://github.com/angular/angular/issues/34352).
+
+Your alternatives are to use
+- [WorkboxJS](https://developers.google.com/web/tools/workbox/){.text-blue .underline}
+- Follow the discussion in [this issue](https://github.com/angular/angular/issues/34352){.text-blue .underline} and [here](https://github.com/angular/angularfire/discussions/1923){.text-blue .underline}, manually registering the Angular Service Worker
+- The Firebase Messaging Service Worker, which is detailed below
+
+## Import the `NgModule`
+
+Push Notifications for AngularFire are contained in the `@angular/fire/messaging` module namespace. Import the `AngularFireMessagingModule` in your `NgModule`. This sets up the `AngularFireMessaging` service for dependency injection.
+
+```ts
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+import { AppComponent } from './app.component';
+import { AngularFireModule } from '@angular/fire';
+import { AngularFireMessagingModule } from '@angular/fire/messaging';
+import { environment } from '../environments/environment';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ AngularFireModule.initializeApp(environment.firebase),
+ AngularFireMessagingModule
+ ],
+ declarations: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule {}
+```
+
+### Setting up the Firebase Messaging Service Worker
+
+There are two parts to Firebase Messaging, a Service Worker and the DOM API. AngularFireMessaging allows you to request permission, get tokens, delete tokens, and subscribe to messages on the DOM side. To register to receive notifications you need to set up the Service Worker. [The official Firebase documentation for setting up the details exactly how to do that](https://firebase.google.com/docs/cloud-messaging/js/client).
+
+You can either use the `firebase-messaging-sw.js` file provided in the docs or you can set your own Service Worker to import that script. Make sure to set up your `angular.json` file to copy over the Service Worker file:
+
+```json
+ "assets": [
+ "assets",
+ "favicon.ico",
+ "firebase-messaging-sw.js",
+ "manifest.json"
+ ],
+```
+
+[Warning] Remember update the `firebase-messaging-sw.js` everytime you update the `firebase` in package.json. The missmatch version could lead to unable to receive notification in `foreground`, you can create your `firebase-messaging-sw.js` like this:
+
+```js
+// Give the service worker access to Firebase Messaging.
+// Note that you can only use Firebase Messaging here, other Firebase libraries
+// are not available in the service worker.
+importScripts('https://www.gstatic.com/firebasejs/[the%20number%20of%20version%20matching%20with%20firebase%20in%20package.json]/firebase-app.js');
+importScripts('https://www.gstatic.com/firebasejs/[for%20example:%208.2.6]/firebase-messaging.js');
+
+// Initialize the Firebase app in the service worker by passing in the
+// messagingSenderId.
+
+firebase.initializeApp({
+ apiKey: '',
+ authDomain: '',
+ databaseURL: '',
+ projectId: '',
+ storageBucket: '',
+ messagingSenderId: ''
+});
+
+// Retrieve an instance of Firebase Messaging so that it can handle background
+// messages.
+const messaging = firebase.messaging();
+```
+
+### Requesting permission
+
+Once you have the Firebase Messaging Service Worker set up and installed, you need to request permission to send a user notifications. While the browser will popup a UI for you, it is highly recommend to ask the user for permission with a custom UI and only ask when it makes sense. If you blindly ask for permission, you have an extremely high chance of getting denied or blocked.
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFireMessaging } from '@angular/fire/messaging';
+
+@Component({
+ selector: 'app-root',
+ template: `
+
+ `
+})
+export class AppComponent {
+ constructor(private afMessaging: AngularFireMessaging) { }
+ requestPermission() {
+ this.afMessaging.requestPermission
+ .subscribe(
+ () => { console.log('Permission granted!'); },
+ (error) => { console.error(error); },
+ );
+ }
+}
+```
+
+Once you have the permission of the user, you need their token. You can do this with the `getToken` observable or the `tokenChanges` observable. The `tokenChanges` observable listens for token refreshes whereas the `getToken` observable is a one-time call.
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFireMessaging } from '@angular/fire/messaging';
+import { mergeMapTo } from 'rxjs/operators';
+
+@Component({
+ selector: 'app-root',
+ template: `
+
+ `
+})
+export class AppComponent {
+ constructor(private afMessaging: AngularFireMessaging) { }
+ requestPermission() {
+ this.afMessaging.requestPermission
+ .pipe(mergeMapTo(this.afMessaging.tokenChanges))
+ .subscribe(
+ (token) => { console.log('Permission granted! Save to the server!', token); },
+ (error) => { console.error(error); },
+ );
+ }
+}
+```
+
+Once you have a user's token, you need to save it to the server in order to send them notifications in response to events. Let's say you want to send a push each time a user sends a chat message. Once a user grants permission, you can send the token to the Realtime Database or Cloud Firestore and associate it with a unique id, like a Firebase Auth UID. You can then create a Cloud Function trigger that looks up the user's token when a chat message is created.
+
+### Shortcutting token requests
+
+An easier way of requesting permission and getting tokens is with the `requestToken` observable. It combines the two steps above into one observable.
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFireMessaging } from '@angular/fire/messaging';
+
+@Component({
+ selector: 'app-root',
+ template: `
+
+ `
+})
+export class AppComponent {
+ constructor(private afMessaging: AngularFireMessaging) { }
+ requestPermission() {
+ this.afMessaging.requestToken
+ .subscribe(
+ (token) => { console.log('Permission granted! Save to the server!', token); },
+ (error) => { console.error(error); },
+ );
+ }
+}
+```
+
+The `requestToken` observable uses the `tokenChanges` observable to listen to refreshes.
+
+### Deleting tokens
+
+Need to delete a user's token? Not a problem.
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFireMessaging } from '@angular/fire/messaging';
+import { mergeMap } from 'rxjs/operators';
+
+@Component({
+ selector: 'app-root',
+ template: `
+
+ `
+})
+export class AppComponent {
+ constructor(private afMessaging: AngularFireMessaging) { }
+ deleteToken() {
+ this.afMessaging.getToken
+ .pipe(mergeMap(token => this.afMessaging.deleteToken(token)))
+ .subscribe(
+ (token) => { console.log('Token deleted!'); },
+ );
+ }
+}
+```
+
+The code above requests the current user's token and passes it to the `deleteToken()` observable.
+
+### Subscribing to foreground messages
+
+Once you have a user's token and they are subscribed, you can listen to messages in the foreground. The Firebase Messaging Service Worker handles background push notifications.
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFireMessaging } from '@angular/fire/messaging';
+
+@Component({
+ selector: 'app-root',
+ template: `
+
+ `
+})
+export class AppComponent {
+ constructor(private afMessaging: AngularFireMessaging) { }
+ listen() {
+ this.afMessaging.messages
+ .subscribe((message) => { console.log(message); });
+ }
+}
+```
+
+### Sending notifications
+
+[Sending a notification](https://firebase.google.com/docs/cloud-messaging/js/first-message) requires a call to a server. You can do this directly with an HTTP call or you can even build a Cloud Function to do this in response to an event. A Cloud Function trigger is ideal because you have trusted access to the database and can securely look up tokens to send to the right user. If you want to send push notifications via HTTP requests you'll need to secure the API call. This is usually done with a Firebase Auth UID. On the server you can verify the UID with the Firebase Admin SDK and allow access to get a user's push id.
+
+The [Firebase Admin SDK has helper functions for sending notifications](https://firebase.google.com/docs/cloud-messaging/admin/send-messages) to the user and subscribing them to topics, which [simplifies sending grouped messages](https://firebase.google.com/docs/cloud-messaging/admin/manage-topic-subscriptions).
\ No newline at end of file
diff --git a/site/src/messaging/index.md b/site/src/messaging/index.md
new file mode 100644
index 000000000..8bf754c30
--- /dev/null
+++ b/site/src/messaging/index.md
@@ -0,0 +1,6 @@
+---
+eleventyNavigation:
+ key: Messaging
+ order: 8
+---
+
diff --git a/site/src/messaging/messaging.11tydata.json b/site/src/messaging/messaging.11tydata.json
new file mode 100644
index 000000000..b924da1f3
--- /dev/null
+++ b/site/src/messaging/messaging.11tydata.json
@@ -0,0 +1,4 @@
+{
+ "layout": "guide.njk",
+ "tags": "guides"
+}
diff --git a/site/src/performance/getting-started.md b/site/src/performance/getting-started.md
new file mode 100644
index 000000000..5de106ca2
--- /dev/null
+++ b/site/src/performance/getting-started.md
@@ -0,0 +1,131 @@
+---
+title: Getting started
+eleventyNavigation:
+ key: Getting started
+ parent: Performance
+---
+
+## Automatic page load tracing
+
+Understand your Angular application's real-world performance with [Firebase Performance Monitoring](https://firebase.google.com/docs/perf-mon). Performance Monitoring automatically provides a trace for **page load** when you add `AngularFirePerformanceModule` into your App Module's imports.
+
+```ts
+import { AngularFireModule } from '@angular/fire';
+import { AngularFirePerformanceModule, PerformanceMonitoringService } from '@angular/fire/performance';
+import { environment } from '../environments/environment';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ AngularFireModule.initializeApp(environment.firebase),
+ AngularFirePerformanceModule,
+ ...
+ ],
+ providers: [
+ PerformanceMonitoringService
+ ],
+ declarations: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule {}
+```
+
+The page load trace breaks down into the following default metrics:
+
+* [First paint traces](https://firebase.google.com/docs/perf-mon/automatic-web#first-paint){.text-blue .underline} — measure the time between when the user navigates to a page and when any visual change happens
+* [First contentful paint traces](https://firebase.google.com/docs/perf-mon/automatic-web#contentful-paint){.text-blue .underline} — measure the time between when a user navigates to a page and when meaningful content displays, like an image or text
+* [domInteractive traces](https://firebase.google.com/docs/perf-mon/automatic-web#domInteractive){.text-blue .underline} — measure the time between when the user navigates to a page and when the page is considered interactive for the user
+* [domContentLoadedEventEnd traces](https://firebase.google.com/docs/perf-mon/automatic-web#domContentLoaded){.text-blue .underline} — measure the time between when the user navigates to a page and when the initial HTML document is completely loaded and parsed
+* [loadEventEnd traces](https://firebase.google.com/docs/perf-mon/automatic-web#loadEventEnd){.text-blue .underline} — measure the time between when the user navigates to the page and when the current document's load event completes
+* [First input delay traces](https://firebase.google.com/docs/perf-mon/automatic-web#input-delay){.text-blue .underline} — measure the time between when the user interacts with a page and when the browser is able to respond to that input
+* *Angular specific traces* - `PerformanceMonitoringService` will measure the time needed for `ApplicationRef.isStable` to be true, an important metric to track if you're concerned about solving Zone.js issues for proper functionality of NGSW and Server Side Rendering
+
+## Measuring First Input Delay
+
+First Input Delay (FID) measures the time from when a user first interacts with your site (i.e. when they click a link, tap on a button, or use a custom, JavaScript-powered control) to the time when the browser is actually able to respond to that interaction. [See the article on the Google Developer's Blog for more information on FID.](https://developers.google.com/web/updates/2018/05/first-input-delay)
+
+In order to track first input delay, you'll want to [polyfill the browser performance API](https://github.com/GoogleChromeLabs/first-input-delay):
+
+```bash
+npm install --save-dev first-input-delay
+```
+
+Then add `import 'first-input-delay';` to your `src/polyfills.ts`.
+
+## Manual traces
+
+You can inject `AngularFirePerformance` to perform manual traces.
+
+```ts
+constructor(private performance: AngularFirePerformance) {}
+
+// ...
+
+const trace = await this.performance.trace('some-trace');
+trace.start();
+// Dome something you want to trace
+trace.stop();
+```
+
+## RxJS operators
+
+AngularFire provides a number of RxJS operators which wrap the User Timing API. These are picked up by performance monitoring tools such as Chrome Inspector and Firebase Performance Monitoring.
+
+```ts
+import { trace } from '@angular/fire/performance';
+
+// ...
+
+constructor(private performance: AngularFirePerformance, private afs: AngularFirestore) {}
+
+ngOnInit() {
+ this.articles = afs.collection('articles')
+ .collection('articles', ref => ref.orderBy('publishedAt', 'desc'))
+ .snapshotChanges()
+ .pipe(
+ // measure the amount of time between the Observable being subscribed to and first emission (or completion)
+ trace('getArticles'),
+ map(articles => ...)
+ );
+}
+```
+
+### `trace(name: string)`
+
+The most basic operator, `trace` will measure the amount of time it takes for your observable to either complete or emit its first value. Beyond the basic trace there are several other operators:
+
+```ts
+traceUntil(
+ name: string,
+ test: (T) => Boolean,
+ options?: { orComplete?: true }
+)
+```
+
+Trace the observable until the first emission that passes the provided test.
+
+If the `orComplete` option is passed it will complete the trace when the observable completes, even if an emission never passed the provided test.
+
+```ts
+traceWhile(
+ name: string,
+ test: (T) => Boolean,
+ options?: { orComplete?: true }
+)
+```
+
+Starting with an emission that passes the provided test, trace until an emission fails the test.
+
+If the `orComplete` option is passed it will complete any existing trace when the observable completes.
+
+### `traceUntilLast(name: string)`
+
+Trace the observable until completion.
+
+### `traceUntilFirst(name: string)`
+
+Traces the observable until the first emission.
+
+## Configuration via Dependency Injection
+
+Set `INSTRUMENTATION_ENABLED` or `DATA_COLLECTION_ENABLED` to false disable all automatic and custom traces respectively.
diff --git a/site/src/performance/index.md b/site/src/performance/index.md
new file mode 100644
index 000000000..7a6ef7fce
--- /dev/null
+++ b/site/src/performance/index.md
@@ -0,0 +1,5 @@
+---
+eleventyNavigation:
+ key: Performance
+ order: 10
+---
diff --git a/site/src/performance/performance.11tydata.json b/site/src/performance/performance.11tydata.json
new file mode 100644
index 000000000..b924da1f3
--- /dev/null
+++ b/site/src/performance/performance.11tydata.json
@@ -0,0 +1,4 @@
+{
+ "layout": "guide.njk",
+ "tags": "guides"
+}
diff --git a/site/src/remote-config/getting-started.md b/site/src/remote-config/getting-started.md
new file mode 100644
index 000000000..e95d71649
--- /dev/null
+++ b/site/src/remote-config/getting-started.md
@@ -0,0 +1,132 @@
+---
+title: Getting started
+eleventyNavigation:
+ key: Getting started
+ parent: Remote Config
+---
+
+## Getting started with Remote Config (BETA)
+
+`AngularFireRemoteConfig` dynamically imports the `firebase/remote-config` library on demand, provides convenience observables, pipes, and a promisified version of the [Firebase Remote Config SDK (`firebase.remoteConfig.RemoteConfig`)](https://firebase.google.com/docs/reference/js/firebase.remoteconfig.RemoteConfig).
+
+## API
+
+```ts
+class AngularFireRemoteConfigModule { }
+
+interface ConfigTemplate {[key:string]: string|number|boolean}
+
+type Parameter extends remoteConfig.Value {
+ key: string,
+ fetchTimeMillis: number
+}
+
+class AngularFireRemoteConfig {
+ changes: Observable;
+ parameters: Observable;
+ numbers: Observable<{[key:string]: number|undefined}> & {[key:string]: Observable};
+ booleans: Observable<{[key:string]: boolean|undefined}> & {[key:string]: Observable};
+ strings: Observable<{[key:string]: string|undefined}> & {[key:string]: Observable};
+
+ // from firebase.remoteConfig() proxy:
+ activate: () => Promise;
+ ensureInitialized: () => Promise;
+ fetch: () => Promise;
+ fetchAndActivate: () => Promise;
+ getAll: () => Promise<{[key:string]: remoteConfig.Value}>;
+ getBoolean: (key:string) => Promise;
+ getNumber: (key:string) => Promise;
+ getString: (key:string) => Promise;
+ getValue: (key:string) => Promise;
+ setLogLevel: (logLevel: remoteConfig.LogLevel) => Promise;
+ settings: Promise;
+ defaultConfig: Promise<{[key: string]: string | number | boolean}>;
+ fetchTimeMillis: Promise;
+ lastFetchStatus: Promise;
+}
+
+// Pipes for working with .changes and .parameters
+filterRemote: () => MonoTypeOperatorFunction
+filterFresh: (interval: number) => MonoTypeOperatorFunction
+budget: (interval: number) => MonoTypeOperatorFunction
+
+// scanToObject is for use with .changes
+scanToObject: () => OperatorFunction
+
+// mapToObject is the same behavior as scanToObject but for use with .parameters
+mapToObject: () => OperatorFunction
+
+SETTINGS = InjectionToken;
+DEFAULTS = InjectionToken;
+```
+
+Using the `SETTINGS` DI Token (*default: {}*) will allow you to [configure Firebase Remote Config](https://firebase.google.com/docs/reference/js/firebase.remoteconfig.Settings.html).
+
+## Configure default values
+
+Providing `DEFAULTS ({[key: string]: string | number | boolean})` tells `AngularFireRemoteConfig` to emit the provided defaults first. This allows you to count on Remote Config when the user is offline or in environments that the Remote Config service does not handle (i.e. Server Side Rendering).
+
+```ts
+import { AngularFireRemoteConfigModule, DEFAULTS, SETTINGS } from '@angular/fire/remote-config';
+
+@NgModule({
+ imports: [
+ AngularFireModule.initializeApp(environment.firebase),
+ AngularFireRemoteConfigModule
+ ],
+ providers: [
+ { provide: DEFAULTS, useValue: { enableAwesome: true } },
+ {
+ provide: SETTINGS,
+ useFactory: () => isDevMode() ? { minimumFetchIntervalMillis: 10_000 } : {}
+ }
+ ]
+})
+export class AppModule { }
+...
+
+constructor(remoteConfig: AngularFireRemoteConfig) {
+ remoteConfig.changes.pipe(
+ filterFresh(172_800_000), // ensure we have values from at least 48 hours ago
+ first(),
+ // scanToObject when used this way is similar to defaults
+ // but most importantly smart-casts remote config values and adds type safety
+ scanToObject({
+ enableAwesome: true,
+ titleBackgroundColor: 'blue',
+ titleFontSize: 12
+ })
+ ).subscribe(…);
+
+ // all remote config values cast as strings
+ remoteConfig.strings.subscribe(...)
+ remoteConfig.booleans.subscribe(...); // as booleans
+ remoteConfig.numbers.subscribe(...); // as numbers
+
+ // convenience for observing a single string
+ remoteConfig.strings.titleBackgroundColor.subscribe(...);
+ remoteConfig.booleans.enableAwesome.subscribe(...); // boolean
+ remoteConfig.numbers.titleBackgroundColor.subscribe(...); // number
+
+ // however those may emit more than once as the remote config cache fires and gets fresh values
+ // from the server. You can filter it out of .changes for more control:
+ remoteConfig.changes.pipe(
+ filter(param => param.key === 'titleBackgroundColor'),
+ map(param => param.asString())
+ // budget at most 800ms and return the freshest value possible in that time
+ // our budget pipe is similar to timeout but won't error or abort the pending server fetch
+ // (it won't emit it, if the deadline is exceeded, but it will have been fetched so can use the
+ // freshest values on next subscription)
+ budget(800),
+ last()
+ ).subscribe(...)
+
+ // just like .changes, but scanned into an array
+ remoteConfig.parameters.subscribe(all => ...);
+
+ // or make promisified firebase().remoteConfig() calls direct off AngularFireRemoteConfig
+ // using our proxy
+ remoteConfig.getAll().then(all => ...);
+ remoteConfig.lastFetchStatus.then(status => ...);
+}
+```
diff --git a/site/src/remote-config/index.md b/site/src/remote-config/index.md
new file mode 100644
index 000000000..1334bae3a
--- /dev/null
+++ b/site/src/remote-config/index.md
@@ -0,0 +1,5 @@
+---
+eleventyNavigation:
+ key: Remote Config
+ order: 9
+---
diff --git a/site/src/remote-config/remote-config.11tydata.json b/site/src/remote-config/remote-config.11tydata.json
new file mode 100644
index 000000000..b924da1f3
--- /dev/null
+++ b/site/src/remote-config/remote-config.11tydata.json
@@ -0,0 +1,4 @@
+{
+ "layout": "guide.njk",
+ "tags": "guides"
+}
diff --git a/site/src/rtdb/index.md b/site/src/rtdb/index.md
new file mode 100644
index 000000000..7cb869514
--- /dev/null
+++ b/site/src/rtdb/index.md
@@ -0,0 +1,5 @@
+---
+eleventyNavigation:
+ key: RTDB
+ order: 4
+---
diff --git a/site/src/rtdb/lists.md b/site/src/rtdb/lists.md
new file mode 100644
index 000000000..220c7d7ae
--- /dev/null
+++ b/site/src/rtdb/lists.md
@@ -0,0 +1,252 @@
+---
+title: Lists
+eleventyNavigation:
+ key: Lists
+ parent: RTDB
+---
+
+## Retrieving data as lists
+
+AngularFire synchronizes data as lists using the `AngularFireList` service. The `AngularFireList` service is not created by itself, but through the `AngularFireDatabase` service. The guide below demonstrates how to retrieve, save, and remove data as lists.
+
+## Injecting the `AngularFireDatabase` service
+
+*Make sure you have bootstrapped your application for AngularFire. See the Installation guide for bootstrap setup.*
+
+AngularFireDatabase is a service which can be injected through the constructor of your Angular component or `@Injectable()` service. In the previous step, we modified the `/src/app/app.component.ts` to retrieve data as an object. In this step, let's start with a clean slate.
+
+Replace your `/src/app/app.component.ts` from previous step to look like below.
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFireDatabase } from '@angular/fire/database';
+
+@Component({
+ selector: 'app-root',
+ templateUrl: 'app.component.html',
+ styleUrls: ['app.component.css']
+})
+export class AppComponent {
+ constructor(db: AngularFireDatabase) { }
+}
+```
+
+In this section, we're going to modify the `/src/app/app.component.ts` to retrieve data as list, but before that let's look at ways around how to bind to a list.
+
+## Create a list binding
+
+Data is retrieved through the `AngularFireDatabase` service. The service is also generic. Provide the singular type and not the array type.
+
+```ts
+const listRef = db.list('items');
+const shirtsRef = db.list('shirts');
+```
+
+### Retrieve data
+
+To get the list in realtime, create a list binding as a property of your component or service. Then in your template, you can use the `async` pipe to unwrap the binding.
+
+Update `/src/app/app.component.ts` to import `AngularFireList` from `@angular/fire` and iterate through the list once data is retrieved. Also note the change in attribute `templateUrl` to inline `template` below.
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFireDatabase } from '@angular/fire/database';
+import { Observable } from 'rxjs';
+
+@Component({
+ selector: 'app-root',
+ template: `{%raw%}
+
+
+ {{ item | json }}
+
+
+ {%endraw%}`,
+})
+export class AppComponent {
+ items: Observable;
+ constructor(db: AngularFireDatabase) {
+ this.items = db.list('items').valueChanges();
+ }
+}
+```
+
+## `AngularFireAction` - Action based API
+
+AngularFire provides methods that stream data back as redux compatible actions. This gives you extra horsepower when using libraries like Animations, ngrx, and ReactiveForms.
+
+### `valueChanges()`
+
+*What is it?* - Returns an Observable of data as a synchronized array of JSON objects. All Snapshot metadata is stripped and just the method provides only the data.
+
+*Why would you use it?* - When you just need a list of data. No snapshot metadata is attached to the resulting array which makes it simple to render to a view.
+
+*When would you not use it?* - When you need a more complex data structure than an array or you need the `key` of each snapshot for data manipulation methods. This method assumes you either are saving the `key` for the snapshot data or using a "readonly" approach.
+
+### `snapshotChanges()`
+
+*What is it?* - Returns an Observable of data as a synchronized array of `AngularFireAction[]`.
+
+*Why would you use it?* - When you need a list of data but also want to keep around metadata. Metadata provides you the underyling `DatabaseReference` and snapshot key. Having the snapshot's `key` around makes it easier to use data manipulation methods. This method gives you more horsepower with other Angular integrations such as ngrx, forms, and animations due to the `type` property. The `type` property on each `AngularFireAction` is useful for ngrx reducers, form states, and animation states.
+
+*When would you not use it?* - When you need a more complex data structure than an array or if you need to process changes as they occur. This array is synchronized with the remote and local changes in the Firebase Database.
+
+### `stateChanges()`
+
+*What is it?* - Returns an Observable of the most recent change as an `AngularFireAction`.
+
+*Why would you use it?* - The above methods return a singular `AngularFireAction` from each child event that occurs. `stateChanges()` emits changes as they occur rather than syncing the query order. This works well for ngrx integrations as you can build your own data structure in your reducer methods.
+
+*When would you not use it?* - When you just need a list of data. This is a more advanced usage of `AngularFireDatabase`.
+
+### `auditTrail()`
+
+*What is it?* - Returns an Observable of `AngularFireAction[]` as they occur. Similar to `stateChanges()`, but instead it keeps around the trail of events as an array.
+
+*Why would you use it?* - This method is like `stateChanges()` except it is not ephemeral. It collects each change in an array as they occur. This is useful for ngrx integrations where you need to replay the entire state of an application. This also works as a great debugging tool for all applications. You can simple write `db.list('items').auditTrail().subscribe(console.log)` and check the events in the console as they occur.
+
+*When would you not use it?* - When you just need a list of data. This is a more advanced usage of AngularFireDatabase.
+
+### Limiting events
+
+There are four child events: `"child_added"`, `"child_changed"`, `"child_removed"`, and `"child_moved"`. Each streaming method listens to all four by default. However, your site may only be intrested in one of these events. You can specify which events you'd like to use through the first parameter of each method:
+
+```ts
+this.itemsRef = db.list('items');
+this.itemsRef.snapshotChanges(['child_added'])
+ .subscribe(actions => {
+ actions.forEach(action => {
+ console.log(action.type);
+ console.log(action.key);
+ console.log(action.payload.val());
+ });
+ });
+```
+
+## API Summary
+
+The table below highlights some of the common methods on the `AngularFireList`.
+
+| method | |
+| ---------|--------------------|
+| `push(value: T)` | Creates a new record on the list, using the Realtime Database's push-ids. |
+| `update(keyRefOrSnap: string, value: T)` | Firebase | AFUnwrappedSnapshot, value: Object) | Updates an existing item in the array. Accepts a key, database reference, or an unwrapped snapshot. |
+| `remove(key: string?)` | Deletes the item by key. If no parameter is provided, the entire list will be deleted. |
+
+## Returning promises
+
+Each data operation method in the table above returns a promise. However,
+you should rarely need to use the completion promise to indicate success,
+because the realtime database keeps the list in sync.
+
+The promise can be useful to chain multiple operations, catching possible errors
+from security rules denials, or for debugging.
+
+```ts
+const promise = db.list('items').remove();
+promise
+ .then(_ => console.log('success'))
+ .catch(err => console.log(err, 'You do not have access!'));
+```
+
+## Adding new items
+
+Use the `push()` method to add new items on the list.
+
+```ts
+const itemsRef = db.list('items');
+itemsRef.push({ name: newName });
+```
+
+### Replacing items in the list using `set`
+
+Use the `set()` method to update existing items.
+
+```ts
+const itemsRef = db.list('items');
+// to get a key, check the Example app below
+itemsRef.set('key-of-some-data', { size: newSize });
+```
+
+Replaces the current value in the database with the new value specified as the parameter. This is called a destructive update, because it deletes everything currently in place and saves the new value.
+
+## Updating items in the list using `update`
+
+Use the `update()` method to update existing items.
+
+```ts
+const itemsRef = db.list('items');
+// to get a key, check the Example app below
+itemsRef.update('key-of-some-data', { size: newSize });
+```
+
+Note that this updates the current value with in the database with the new value specified as the parameter. This is called a non-destructive update, because it only updates the values specified.
+
+### Removing items from the list
+Use the `remove()` method to remove data at the list item's location.
+
+```ts
+const itemsRef = db.list('items');
+// to get a key, check the Example app below
+itemsRef.remove('key-of-some-data');
+```
+
+## Deleting the entire list
+
+If you omit the `key` parameter from `.remove()` it deletes the entire list.
+
+```ts
+const itemsRef = db.list('items');
+itemsRef.remove();
+```
+
+The following is a complete example of deleting an entire list.
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFireDatabase, AngularFireList } from '@angular/fire/database';
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+@Component({
+ selector: 'app-root',
+ template: `
+
+
+
+
+
+
+
+
+
+
+ `,
+})
+export class AppComponent {
+ itemsRef: AngularFireList;
+ items: Observable;
+ constructor(db: AngularFireDatabase) {
+ this.itemsRef = db.list('messages');
+ // Use snapshotChanges().map() to store the key
+ this.items = this.itemsRef.snapshotChanges().pipe(
+ map(changes =>
+ changes.map(c => ({ key: c.payload.key, ...c.payload.val() }))
+ )
+ );
+ }
+ addItem(newName: string) {
+ this.itemsRef.push({ text: newName });
+ }
+ updateItem(key: string, newText: string) {
+ this.itemsRef.update(key, { text: newText });
+ }
+ deleteItem(key: string) {
+ this.itemsRef.remove(key);
+ }
+ deleteEverything() {
+ this.itemsRef.remove();
+ }
+}
+```
+
diff --git a/site/src/rtdb/objects.md b/site/src/rtdb/objects.md
new file mode 100644
index 000000000..efd9592bd
--- /dev/null
+++ b/site/src/rtdb/objects.md
@@ -0,0 +1,179 @@
+---
+title: Objects
+eleventyNavigation:
+ key: Objects
+ parent: RTDB
+---
+
+## Retrieving data as objects
+
+The `AngularFireObject` is a service for manipulating and streaming object data. The `AngularFireObject` service is not created by itself, but through the `AngularFireDatabase` service.
+
+## Injecting the `AngularFireDatabase` service
+
+*Make sure you have bootstrapped your application for AngularFire. See the Installation guide for bootstrap setup.*
+
+`AngularFireDatabase` is a service which can be injected through the constructor of your Angular component or `@Injectable()` service.
+
+If you've followed the earlier step "Installation and Setup" your `/src/app/app.component.ts` should look like below.
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFireDatabase } from '@angular/fire/database';
+import { Observable } from 'rxjs';
+
+@Component({
+ selector: 'app-root',
+ templateUrl: 'app.component.html',
+ styleUrls: ['app.component.css']
+})
+export class AppComponent {
+ items: Observable;
+ constructor(db: AngularFireDatabase) {
+ this.items = db.list('items').valueChanges();
+ }
+}
+```
+
+In this section, we're going to modify the `/src/app/app.component.ts` to retrieve data as object.
+
+## Create an object binding
+
+```ts
+const relative = db.object('item').valueChanges();
+```
+
+## Retrieve data
+
+To get the object in realtime, create an object binding as a property of your component or service.
+
+Then in your template, you can use the `async` pipe to unwrap the binding.
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFireDatabase } from '@angular/fire/database';
+import { Observable } from 'rxjs';
+
+@Component({
+ selector: 'app-root',
+ template: `{%raw%}
+
{{ (item | async)?.name }}
+ {%endraw%}`,
+})
+export class AppComponent {
+ item: Observable;
+ constructor(db: AngularFireDatabase) {
+ this.item = db.object('item').valueChanges();
+ }
+}
+```
+
+## API Summary
+
+The table below highlights some of the common methods on the `AngularFireObject`.
+
+| method | |
+| ---------|--------------------|
+| `set(value: T)` | Replaces the current value in the database with the new value specified as the parameter. This is called a **destructive** update, because it deletes everything currently in place and saves the new value. |
+| `update(value: T)` | Updates the current value with in the database with the new value specified as the parameter. This is called a **non-destructive** update, because it only updates the values specified. |
+| `remove()` | Deletes all data present at that location. Same as calling `set(null)`. |
+
+## Returning promises
+
+Each data operation method in the table above returns a promise. However,
+you should rarely need to use the completion promise to indicate success,
+because the realtime database keeps the object in sync.
+
+The promise can be useful to chain multiple operations, catching possible errors from security rules denials, or for debugging.
+
+```ts
+const promise = db.object('item').remove();
+promise
+ .then(_ => console.log('success'))
+ .catch(err => console.log(err, 'You dont have access!'));
+```
+
+## Saving data
+
+Use the `set()` method for **destructive updates**.
+
+```ts
+const itemRef = db.object('item');
+itemRef.set({ name: 'new name!'});
+```
+
+## Updating data
+
+Use the `update()` method for **non-destructive updates**.
+
+```ts
+const itemRef = db.object('item');
+itemRef.update({ age: newAge });
+```
+
+**Only objects are allowed for updates, not primitives**. This is because
+using an update with a primitive is the exact same as doing a `.set()` with a primitive.
+
+## Deleting data
+
+Use the `remove()` method to remove data at the object's location.
+
+```ts
+const itemRef = db.object('item');
+itemRef.remove();
+```
+
+**Example app**:
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFireDatabase, AngularFireObject } from '@angular/fire/database';
+import { Observable } from 'rxjs';
+
+@Component({
+ selector: 'app-root',
+ template: `{%raw%}
+
{{ item | async | json }}
+
+
+
+
+
+
+ {%endraw%}`,
+})
+export class AppComponent {
+ itemRef: AngularFireObject;
+ item: Observable;
+ constructor(db: AngularFireDatabase) {
+ this.itemRef = db.object('item');
+ this.item = this.itemRef.valueChanges();
+ }
+ save(newName: string) {
+ this.itemRef.set({ name: newName });
+ }
+ update(newSize: string) {
+ this.itemRef.update({ size: newSize });
+ }
+ delete() {
+ this.itemRef.remove();
+ }
+}
+```
+
+## Retrieving the snapshot
+
+AngularFire `valueChanges()` unwraps the Firebase DataSnapshot by default, but you can get the data as the original snapshot by using the `snapshotChanges()` option.
+
+```ts
+this.itemRef = db.object('item');
+this.itemRef.snapshotChanges().subscribe(action => {
+ console.log(action.type);
+ console.log(action.key)
+ console.log(action.payload.val())
+});
+```
+
+## Querying?
+
+Because `AngularFireObject` synchronizes objects from the realtime database, sorting will have no effect for queries that are not also limited by a range. For example, when paginating you would provide a query with a sort and filter. Both the sort operation and the filter operation affect which subset of the data is returned by the query; however, because the resulting object is simply json, the sort order will not be preseved locally. Hence, for operations that require sorting, you are probably looking for a list.
diff --git a/site/src/rtdb/querying.md b/site/src/rtdb/querying.md
new file mode 100644
index 000000000..3ba61b55d
--- /dev/null
+++ b/site/src/rtdb/querying.md
@@ -0,0 +1,161 @@
+---
+title: Querying
+eleventyNavigation:
+ key: Querying
+ parent: RTDB
+---
+
+## Querying lists
+
+Lists of data in the Realtime Database can be filtered down using specific querying methods. When these querying methods are combined with RxJS operators you can achieve dynamic querying which re-triggers the query when a source observable changes. This is great for updating queries in response to changes of an input or some other changing value.
+
+## Creating a query with constant values
+
+Queries are created by building on the [`firebase.database.Reference`](https://firebase.google.com/docs/reference/js/firebase.database.Reference).
+
+```ts
+db.list('/items', ref => ref.orderByChild('size').equalTo('large'))
+```
+
+### Query options
+
+| Method | Purpose |
+| ---------|--------------------|
+| `orderByChild` | Specify a child to order by. |
+| `orderByKey` | Boolean to order by Firebase Database keys. |
+| `orderByValue` | Specify a value to order by. |
+| ~~`orderByPriority`~~1 | Boolean to order by Firebase Database priority.|
+| `equalTo`2 | Limit list to items that contain certain value. |
+| `limitToFirst` | Sets the maximum number of items to return from the beginning of the ordered list of results. |
+| `limitToLast` | Sets the maximum number of items to return from the end of the ordered list of results. |
+| `startAt`2 | Return items greater than or equal to the specified key or value, depending on the order-by method chosen. |
+| `endAt`2 | Return items less than or equal to the specified key or value, depending on the order-by method chosen. |
+
+:::annotations-section
+ 1 [This is the old way of doing things and is no longer recommended for use](https://youtu.be/3WTQZV5-roY?t=3m). Anything you can achieve with `orderByPriority` you should be doing with `orderByChild`.
+
+ 2 The Firebase SDK supports an optional `key` parameter for [`startAt`](https://firebase.google.com/docs/reference/js/firebase.database.Reference#startAt), [`endAt`](https://firebase.google.com/docs/reference/js/firebase.database.Reference#endAt), and [`equalTo`](https://firebase.google.com/docs/reference/js/firebase.database.Reference#equalTo) when ordering by child, value, or priority. You can specify the `key` parameter using an object literal that contains the `value` and the `key`. For example: `startAt: { value: 'some-value', key: 'some-key' }`.
+:::
+
+To learn more about how sorting and ordering data works in Firebase, check out the Firebase documentation on [working with lists of data](https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data).
+
+## Invalid query combinations
+
+*Queries can only be ordered by one method.* This means you can only specify
+`orderByChild`, `orderByKey`, `orderByPriority`, or `orderByValue`.
+
+```ts
+// WARNING: Do not copy and paste. This will not work!
+ref.orderByChild('size').equalTo('large').orderByKey(true)
+```
+
+You can only use `limitToFirst` or `limitToLast`, but not both in combination.
+
+```ts
+// WARNING: Do not copy and paste. This will not work!
+ref.limitToFirst(10).limitToLast(100)
+```
+
+## Dynamic querying
+
+To enable dynamic queries one should lean on RxJS Operators like `switchMap`.
+
+An RxJS Subject is imported below. A Subject is like an Observable, but can multicast to many Observers. Subjects are like EventEmitters: they maintain a registry of many listeners. See, [What is a Subject](http://reactivex.io/rxjs/manual/overview.html#subject) for more information.
+
+When we call [`switchMap` on the Subject](https://www.learnrxjs.io/operators/transformation/switchmap.html), we can map each value to a new Observable; in this case a database query.
+
+```ts
+const size$ = new Subject();
+const queryObservable = size$.pipe(
+ switchMap(size =>
+ db.list('/items', ref => ref.orderByChild('size').equalTo(size)).valueChanges()
+ )
+);
+
+// subscribe to changes
+queryObservable.subscribe(queriedItems => {
+ console.log(queriedItems);
+});
+
+// trigger the query
+size$.next('large');
+
+// re-trigger the query
+size$.next('small');
+```
+
+The following is an example of dynamic querying. [See this example in action on StackBlitz](https://stackblitz.com/edit/angularfire-db-api-s8ip7m).
+
+```ts
+import { Component } from '@angular/core';
+import { AngularFireDatabase, AngularFireAction } from '@angular/fire/database';
+import { Observable, Subscription, BehaviorSubject } from 'rxjs';
+import { switchMap } from 'rxjs/operators';
+
+@Component({
+ selector: 'app-root',
+ template: `{%raw%}
+
+ {%endraw%}`,
+})
+export class AppComponent {
+ items$: Observable[]>;
+ size$: BehaviorSubject;
+
+ constructor(db: AngularFireDatabase) {
+ this.size$ = new BehaviorSubject(null);
+ this.items$ = this.size$.pipe(
+ switchMap(size =>
+ db.list('/items', ref =>
+ size ? ref.orderByChild('size').equalTo(size) : ref
+ ).snapshotChanges()
+ )
+ );
+ }
+ filterBy(size: string|null) {
+ this.size$.next(size);
+ }
+}
+```
+
+*To run the above example as is, you need to have sample data in you firebase database with the following structure:*
+
+ ```json
+{
+ "items": {
+ "a" : {
+ "size" : "small",
+ "text" : "small thing"
+ },
+ "b" : {
+ "size" : "medium",
+ "text" : "medium sample"
+ },
+ "c" : {
+ "size" : "large",
+ "text" : "large widget"
+ }
+ }
+}
+ ```
+
diff --git a/site/src/rtdb/rtdb.11tydata.json b/site/src/rtdb/rtdb.11tydata.json
new file mode 100644
index 000000000..b924da1f3
--- /dev/null
+++ b/site/src/rtdb/rtdb.11tydata.json
@@ -0,0 +1,4 @@
+{
+ "layout": "guide.njk",
+ "tags": "guides"
+}
diff --git a/site/src/shortcodes/buttons/index.js b/site/src/shortcodes/buttons/index.js
new file mode 100644
index 000000000..a4761b1d3
--- /dev/null
+++ b/site/src/shortcodes/buttons/index.js
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const linkButton = {
+ name: "linkbutton",
+ type: "addPairedShortcode",
+ create (content, href, type='primary', external=false) {
+ const primaryClass = `link-button inline-block shadow-lg bg-blue text-white text-lg uppercase font-bold font-display tracking-wide rounded-lg px-8 py-3 text-center`;
+ const secondaryClass = `link-button inline-block shadow-lg bg-blue-200 text-black text-lg uppercase font-bold font-display tracking-wide rounded-lg px-8 py-3 text-center`;
+ const cssClass = type === 'primary' ? primaryClass : secondaryClass;
+ const externalAttrs = external ? 'rel="noopener" target="blank"' : '';
+ return `${content}`;
+ }
+}
+
+module.exports = {
+ shortcodes: [
+ linkButton,
+ ]
+};
diff --git a/site/src/shortcodes/disclaimerprod/index.js b/site/src/shortcodes/disclaimerprod/index.js
new file mode 100644
index 000000000..1fe9f9c81
--- /dev/null
+++ b/site/src/shortcodes/disclaimerprod/index.js
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Usage: {% disclaimerprod %}
+const disclaimerprod = {
+ name: "disclaimerprod",
+ type: "addShortcode",
+ create() {
+ return `
+
Beta Site!
+
This is a brand new guide site that is in beta. During this time period we'd love to hear your feedback on our GitHub Discussion board. Please let us know what you think of the usability, content, and any ideas for improvement. All contributors are welcome!
+
`;
+ }
+}
+
+module.exports = {
+ shortcodes: [
+ disclaimerprod
+ ]
+};
diff --git a/site/src/shortcodes/filters/index.js b/site/src/shortcodes/filters/index.js
new file mode 100644
index 000000000..98d8916b9
--- /dev/null
+++ b/site/src/shortcodes/filters/index.js
@@ -0,0 +1,104 @@
+const console = require('console');
+const { resolve } = require('path');
+
+const findByName = {
+ name: "findByName",
+ type: "addNunjucksFilter",
+ create(list, name) {
+ return list.find((item) => item.name === name);
+ }
+};
+
+const log = {
+ name: "log",
+ type: "addNunjucksFilter",
+ create(object, logName) {
+ console.log(logName, object);
+ return object;
+ }
+};
+
+const json = {
+ name: "json",
+ type: "addNunjucksFilter",
+ create(object, spacer = 3) {
+ let cache = [];
+ const json = JSON.stringify(
+ object,
+ (key, value) => {
+ if (typeof value === "object" && value !== null) {
+ if (cache.includes(value)) {
+ return;
+ }
+ cache.push(value);
+ }
+ return value;
+ },
+ spacer
+ );
+ cache = null;
+ return json;
+ }
+};
+
+const findPreviousEntry = {
+ name: "findPreviousEntry",
+ type: "addNunjucksFilter",
+ create(children, eleventyNavigation) {
+ const itemIndex = children.findIndex(entry => entry.key === eleventyNavigation.key);
+ const previousIndex = itemIndex - 1;
+ return children[previousIndex];
+ }
+};
+
+const findNextEntry = {
+ name: "findNextEntry",
+ type: "addNunjucksFilter",
+ create(children, eleventyNavigation) {
+ const itemIndex = children.findIndex(entry => entry.key === eleventyNavigation.key);
+ const nextIndex = itemIndex + 1;
+ return children[nextIndex];
+ }
+};
+
+/**
+ * This filter reads the custom navigation in the global _data/ folder
+ * and merges it with the eleventyNavigation config. Eleventy Navigation
+ * works great for parent folders but it's less good for child navigation
+ * when it comes to "next/prev" routing. This allows us to keep the good
+ * parts of Eleventy Navigation and have a custom child path routing.
+ *
+ * Eventually I'd like to customize Eleventy Navigation to do child routing
+ * because this is extremely inefficient to loop over nav for every page. It
+ * doesn't effect this build too bad though.
+ */
+const mergeNavigation = {
+ name: "mergeNavigation",
+ type: "addNunjucksFilter",
+ create(eleventyNavigation) {
+ const customNavigation = require(resolve(__dirname, '../../_data/nextprev.json'));
+ const customKeys = Object.keys(customNavigation);
+ customKeys.forEach(key => {
+ const eleventyNavMatch = eleventyNavigation.find(item => item.key === key);
+ if(eleventyNavMatch != undefined) {
+ const matchKids = eleventyNavMatch.children;
+ const newKids = customNavigation[key].children.map(child => {
+ return matchKids.find(c => c.key === child.key);
+ });
+ eleventyNavigation.find(item => item.key === key).children = newKids;
+ }
+ });
+ return eleventyNavigation;
+ }
+}
+
+module.exports = {
+ shortcodes: [
+ findByName,
+ log,
+ json,
+ findPreviousEntry,
+ findNextEntry,
+ mergeNavigation,
+ ],
+};
diff --git a/site/src/shortcodes/headings/index.js b/site/src/shortcodes/headings/index.js
new file mode 100644
index 000000000..57f1a72e0
--- /dev/null
+++ b/site/src/shortcodes/headings/index.js
@@ -0,0 +1,40 @@
+/**
+ * Copyright 2020 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Usage: {% headingone %} My title! {% endheadingone %}
+const headingOne = {
+ name: "headingone",
+ type: "addPairedShortcode",
+ create (content) {
+ return `