Why Go Hybrid

Several readers have reached out asking for a practical example of wiring Angular together with Firebase's "hybrid on-device" feature. This integration lets a single app draw on both cloud-hosted AI models and local on-device processing.

At its core, the approach relies on the Firebase AI SDK, which decides where to run an AI task: locally on the user's device whenever that's an option, with a transparent fallback to cloud inference when the local model isn't available or can't handle the job.

  • Lower Latency: Because on-device inference skips the network round-trip, response times drop noticeably. If the user's connection is slow or flaky, the app can still deliver AI functionality when it matters most.

  • Works Offline: Without any network access, local AI models keep running. Users can still count on a baseline set of AI-driven features even when fully disconnected.

  • Better Privacy: Keeping data on the device cuts down on what gets sent to the cloud. Apps that handle sensitive information can reduce their exposure by processing locally whenever feasible.

  • Lower Cloud Spend: Offloading inference work to the device means fewer cloud API calls, which can add up to real savings on AI-related infrastructure costs.

Getting Into the Code

Below is an Angular service that demonstrates how to talk to the Firebase AI API.

Heads up: Swap the placeholder configuration for credentials from your actual Firebase project before running this.

import { Injectable } from '@angular/core';
import { initializeApp } from 'firebase/app';
import { getAI, getGenerativeModel, GoogleAIBackend } from 'firebase/ai';

@Injectable({
  providedIn: 'root',
})
export class AiService {
  private model: any;

  constructor() {
    const firebaseConfig = {
      // your Firebase config here
    };

    const firebaseApp = initializeApp(firebaseConfig);
    const ai = getAI(firebaseApp, { backend: new GoogleAIBackend() });
    this.model = getGenerativeModel(ai, {
      mode: 'prefer_on_device',
      model: 'gemini-2.5-flash',
    });
  }

  async generateTextFromImage(prompt: string, file: File): Promise<string> {
    try {
      const imagePart = await this.fileToGenerativePart(file);
      const result = await this.model.generateContentStream([
        prompt,
        imagePart,
      ]);

      let aggregatedResponse = '';
      for await (const chunk of result.stream) {
        const chunkText = chunk.text();
        aggregatedResponse += chunkText;
      }
      return aggregatedResponse;
    } catch (err: any) {
      console.error(err.name, err.message);
      throw err;
    }
  }

  private async fileToGenerativePart(file: File): Promise<any> {
    const base64EncodedDataPromise = new Promise<string>((resolve) => {
      const reader = new FileReader();
      reader.onloadend = () =>
        resolve((reader.result as string).split(',')[1] || '');
      reader.readAsDataURL(file);
    });
    return {
      inlineData: { data: await base64EncodedDataPromise, mimeType: file.type },
    };
  }
}
Enter fullscreen mode Exit fullscreen mode

Putting It to Work

This snippet shows how to actually invoke the AI service.

[...]
<input type="file" (change)="imageRecognition($event)" />
[...]
Enter fullscreen mode Exit fullscreen mode
[...]
async imageRecognition(event: any) {
  this.imageResponse = '';
  const file: File = event.target.files[0];
  if (file) {
    try {
      this.imageResponse = await this.aiService.generateTextFromImage(
        "Can you describe this image?",file
      );
    } catch (error: any) {
      this.imageResponse = `Error: ${error.message}`;
    }
  }
}
[...]
Enter fullscreen mode Exit fullscreen mode

The service kicks things off by initializing Firebase with your project config. From there, getAI() and getGenerativeModel() act as the gateways into Firebase AI. The former stands up the AI service and accepts options for specifying the backend, while the latter produces a model instance you can work with. The mode: 'prefer_on_device' setting is the linchpin here — it signals to the SDK that on-device execution is the preferred path, assuming the model is present. generateTextFromImage() then wraps the calls to the GenerativeModel (Gemini) to process text and images. These methods contain the AI logic and manage the streaming responses coming back from the model.

What Goes On in Practice

When your code calls imageRecognition(), the AiService first checks for the on-device Gemini model. If it's ready and capable of handling the request, the generation runs entirely locally. If not — due to device constraints, an undownloaded model, or missing support — the SDK automatically pivots to the cloud-hosted Gemini model without any extra work on your part.


This walkthrough gives you a starting point for hybrid on-device AI with Firebase and Angular. Pairing the strengths of cloud and local processing opens the door to faster, more resilient apps that respect user privacy. Be mindful to store API keys safely, build out comprehensive error handling, and watch resource usage closely to keep performance and cloud spend in check.


You can check out what I'm building on GitHub.

Thanks for reading — if you found this useful, a ❤️ goes a long way.
See you next time 👋