I’m trying to design a “future-proof” React/JS setup where the app can switch between different AI backends (local models like WebGPU/WebLLM, API models like OpenAI/Gemini, and maybe even custom workers) without rewriting half the project every time a new model shows up.

I’m not talking about a simple “if (provider === x)” switch. I mean a real architecture that keeps things clean, testable, and not tightly coupled to any single AI vendor.

Stuff I’ve been thinking about and not super happy with:

  • writing a single “AIService” class with huge condition blocks

  • plugging all providers into React Context and it gets messy real fast

  • creating a wrapper for each model but then I end up duplicating logic

  • routing everything through a single API backend but then I lose WebGPU performance and offline ability

  • using something like RxJS or Zustand to manage streams, but still feels like overkill

The main goals:

  • hot-swap between providers at runtime (OpenAI → Gemini → local model)

  • handle streaming responses in a unified way

  • avoid vendor-lock code patterns inside components

  • keep things predictable if I add more models later

  • work nicely with Suspense, Server Components, or React Native if possible

I’ve seen some teams build a full “AI adapter layer,” but everyone seems to do it differently.
So what’s the best practice here in 2025?
How are experienced devs structuring this so their project doesn’t explode as AI providers keep changing every 2 months?

Any architecture patterns, folder structures, or real-world examples are super welcome.

// something like this but not terrible
export class AIProvider {
  constructor(config) { this.config = config }
  stream(prompt) { throw new Error("not implemented") }
  info() { return { name: "base" } }
}

// OpenAI impl
export class OpenAIProvider extends AIProvider {
  async *stream(prompt) {
    // call openai api...
  }
}

// Local WebGPU impl
export class LocalModelProvider extends AIProvider {
  async *stream(prompt) {
    // call webgpu model...
  }
}

I want to know how people who really build AI products keep this clean and extensible.

1 Reply 1

It doesn't serve a good purpose to make relevant code depend on React, unless you provide the providers with reactivity - and probably even then, the result of stream is consumed by React app and that's where React reactivity or any third-party one like Zustand is used. So "plugging all providers into React Context" doesn't make sense.

The case isn't really specific to AI. You need to associate provider names with their implementations. This class design doesn't look terrible. Basically there can be a map at some point:

providers = {
  openai: OpenAIProvider,
  ...

Check registry, factory, service locator patterns for some ideas.

Your Reply

By clicking “Post Your Reply”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.