Build a Real-Time Chat App with Angular and Firebase

Build a Real-Time Chat App with Angular and Firebase

Build a Real-Time Chat App with Angular and Firebase

Build a live chat application that syncs messages in real time across all connected clients using Firebase — no backend server needed.

Features

  • Google OAuth sign-in via Firebase Auth
  • Real-time messages with Firestore onSnapshot
  • Multiple chat rooms
  • Online presence indicators
  • File/image sharing via Firebase Storage

Step 1 — Setup Firebase

npm install @angular/fire firebase
ng add @angular/fire  # connects to your Firebase project
// app.config.ts
import { provideFirebaseApp, initializeApp } from '@angular/fire/app';
import { provideFirestore, getFirestore }    from '@angular/fire/firestore';
import { provideAuth, getAuth }             from '@angular/fire/auth';

export const appConfig: ApplicationConfig = {
  providers: [
    provideFirebaseApp(() => initializeApp(environment.firebase)),
    provideFirestore(() => getFirestore()),
    provideAuth(() => getAuth()),
  ],
};

Step 2 — Auth Service

@Injectable({ providedIn: 'root' })
export class AuthService {
  private auth = inject(Auth);
  readonly user$ = authState(this.auth);

  signInWithGoogle(): Observable<UserCredential> {
    return from(signInWithPopup(this.auth, new GoogleAuthProvider()));
  }

  signOut(): Observable<void> {
    return from(signOut(this.auth));
  }
}

Step 3 — Chat Service with Firestore

@Injectable({ providedIn: 'root' })
export class ChatService {
  private firestore = inject(Firestore);

  getMessages(roomId: string): Observable<Message[]> {
    const messagesRef = collection(this.firestore, `rooms/${roomId}/messages`);
    const q = query(messagesRef, orderBy('createdAt', 'asc'), limitToLast(50));
    return collectionData(q, { idField: 'id' }) as Observable<Message[]>;
  }

  sendMessage(roomId: string, text: string, user: User): Promise<void> {
    const messagesRef = collection(this.firestore, `rooms/${roomId}/messages`);
    return addDoc(messagesRef, {
      text,
      uid:       user.uid,
      photoURL:  user.photoURL,
      displayName: user.displayName,
      createdAt: serverTimestamp(),
    }).then(() => void 0);
  }
}

Step 4 — Chat Room Component

@Component({
  standalone: true,
  imports: [AsyncPipe, FormsModule],
  template: `
    <div class="messages">
      @for (msg of messages$ | async; track msg.id) {
        <div [class.own]="msg.uid === currentUser?.uid">
          <img [src]="msg.photoURL" />
          <p>{{ msg.text }}</p>
        </div>
      }
    </div>
    <form (ngSubmit)="send()">
      <input [(ngModel)]="newMessage" name="msg" placeholder="Type a message..." />
      <button type="submit">Send</button>
    </form>
  `,
})
export class ChatRoomComponent {
  private chat = inject(ChatService);
  private auth = inject(AuthService);
  roomId       = input.required<string>();
  messages$    = this.chat.getMessages(this.roomId());
  currentUser  = inject(Auth).currentUser;
  newMessage   = '';

  send(): void {
    if (!this.newMessage.trim() || !this.currentUser) return;
    this.chat.sendMessage(this.roomId(), this.newMessage, this.currentUser);
    this.newMessage = '';
  }
}
All Comments