How to Add AI to a React Application
A practical guide to integrating AI into a React application, covering frontend architecture, backend communication, API security, streaming, validation, error handling, and production best practices.
React is a popular choice for building interactive AI-powered web applications. It can provide the interface for chatbots, text generators, document assistants, code generation tools, summarization features, and many other AI workflows. However, React itself does not normally communicate directly with an AI provider when that communication requires a private API key.
A typical production architecture separates the React frontend from the AI provider. React handles the user interface and interaction, while a server-side application communicates with the AI API. This separation protects credentials and gives the application a place to perform authentication, validation, rate limiting, usage tracking, and other business logic.
The good news is that adding AI to a React application does not require a complicated architecture at the beginning. A small application can start with one React interface, one backend endpoint, and one AI provider. Additional capabilities can be introduced as the project grows.
How AI Integration Works in React
The basic integration consists of three major parts: the React frontend, a server-side backend, and an AI provider. The user interacts with React, React sends a request to the backend, and the backend communicates with the AI provider.
User
↓
React application
↓
Backend API
↓
AI provider
↓
Backend API
↓
React applicationThe backend can be implemented using many technologies, including Node.js, Express, a serverless function, a framework with server-side capabilities, or a separate API service. React does not require a particular backend technology.
Why React Should Usually Not Call the AI API Directly
The most important reason is API key security. Browser JavaScript is visible to users. If a private AI API key is included in React code, a user can inspect the application, network requests, or bundled JavaScript and potentially obtain the credential.
Incorrect:
React → AI provider
↑
private API key
Recommended:
React → Your backend → AI provider
↑
private keyUsing a backend also provides much more control. The server can decide which model is used, limit requests, validate input, enforce user permissions, calculate usage, and prevent clients from changing security-sensitive parameters.
What Can You Add AI to a React Application For?
- AI chat interfaces
- Text generation
- Text summarization
- Content rewriting
- Translation
- Code generation
- Document analysis
- Text classification
- Natural-language search
- Recommendations
- Data extraction
- Question-answering systems
Step 1: Create the React Interface
Start with the part users will interact with. For a simple text-generation feature, this might be a textarea, a submit button, a loading indicator, and an area for the generated response.
import { useState } from "react";
export function AiGenerator() {
const [prompt, setPrompt] = useState("");
const [result, setResult] = useState("");
const [loading, setLoading] = useState(false);
async function generate() {
// Send request to backend
}
return (
<div>
<textarea
value={prompt}
onChange={(event) => setPrompt(event.target.value)}
/>
<button onClick={generate} disabled={loading}>
{loading ? "Generating..." : "Generate"}
</button>
<div>{result}</div>
</div>
);
}The React component should primarily manage interface state. It does not need to know the AI provider's private credentials or implementation details.
Step 2: Create a Backend Endpoint
The React application needs an endpoint that it can call. For example, the frontend might send a POST request to /api/ai.
POST /api/ai
Request:
{
"prompt": "Explain HTTP caching"
}
Response:
{
"content": "HTTP caching is..."
}The endpoint becomes the boundary between the untrusted browser and the trusted server-side application logic.
Step 3: Send the Request from React
React can use the standard fetch API to communicate with the backend.
const response = await fetch("/api/ai", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
prompt,
}),
});
const data = await response.json();
setResult(data.content);This request does not expose the provider's API key. The browser only knows about your own application endpoint.
Step 4: Validate Input on the Server
The backend should never assume that data received from React is valid. Users can modify requests independently of the interface, so client-side validation cannot be treated as a security boundary.
const body = await request.json();
if (typeof body.prompt !== "string" || !body.prompt.trim()) {
return Response.json(
{ error: "Prompt is required" },
{ status: 400 }
);
}
if (body.prompt.length > 5000) {
return Response.json(
{ error: "Prompt is too long" },
{ status: 400 }
);
}- Validate required fields.
- Validate data types.
- Limit input length.
- Restrict supported operations.
- Limit requested output size.
- Reject malformed requests.
Step 5: Store the AI API Key on the Server
The AI provider credential should be stored as a server-side environment variable.
AI_API_KEY=your-secret-keyThe exact environment-variable mechanism depends on the backend. The important rule is that the secret must only be available to server-side code.
Step 6: Call the AI Provider
After validation, the backend can construct the request to the AI provider. The exact API format depends on the provider and model.
const aiResponse = await fetch(AI_ENDPOINT, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.AI_API_KEY}`,
},
body: JSON.stringify({
model: "your-model",
messages: [
{
role: "user",
content: body.prompt,
},
],
}),
});An official provider SDK can simplify authentication, request construction, typed responses, streaming, and error handling. A direct HTTP request is useful when you want to keep the integration small or provider-independent.
Step 7: Return the AI Response
After receiving the provider response, the backend should extract the data required by the frontend and return a controlled response.
return Response.json({
content: generatedText,
});Avoid returning the provider's complete raw response unless the frontend genuinely needs it. A stable application-level response format makes it easier to change providers later.
The Complete Basic Flow
1. User enters a prompt
2. React validates the form
3. React sends POST /api/ai
4. Backend authenticates the request
5. Backend validates the input
6. Backend constructs the AI request
7. Backend calls the AI provider
8. Backend validates the response
9. Backend returns application data
10. React displays the resultClient-Side Validation vs Server-Side Validation
React should still validate input because immediate feedback creates a better user experience. However, the server must repeat security and business validation.
| Validation | Purpose | Required on server? |
|---|---|---|
| Empty field check | User experience | Yes |
| Maximum input length | Cost and resource control | Yes |
| Allowed operation | Business rules | Yes |
| Authentication | Access control | Yes |
| Usage quota | Cost control | Yes |
| Button disabled state | User experience | No |
Handling Loading States
AI requests can take longer than ordinary API calls, so the interface should clearly communicate that work is in progress.
async function generate() {
setLoading(true);
setResult("");
try {
const response = await fetch("/api/ai", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ prompt }),
});
const data = await response.json();
setResult(data.content);
} finally {
setLoading(false);
}
}A production interface should also disable duplicate submissions or otherwise prevent accidental repeated requests while a generation is running.
Streaming AI Responses
For longer responses, streaming can significantly improve perceived responsiveness. Instead of waiting for the complete result, the backend sends generated content to the browser progressively.
React
↑
│ Chunk 3
│ Chunk 2
│ Chunk 1
│
Backend
↑
AI providerReact can read the response stream and append incoming chunks to the displayed text. This is especially useful for chat interfaces, code generation, long-form writing, and document analysis.
Streaming primarily improves perceived latency. It does not necessarily reduce the total amount of time required for the model to generate the complete response.
Handling AI API Errors
AI providers can return errors because of invalid requests, authentication problems, rate limits, temporary outages, unavailable models, timeouts, or account restrictions. The backend should translate these failures into predictable application responses.
| Problem | Typical response |
|---|---|
| Invalid input | 400 Bad Request |
| Missing authentication | 401 Unauthorized |
| Insufficient permissions | 403 Forbidden |
| Rate limit | 429 Too Many Requests |
| Provider failure | Controlled 5xx response |
| Timeout | Retry or controlled error |
| Unexpected response | Validation failure |
Do not expose unnecessary provider-specific details to users. Log useful diagnostic information on the server while returning a clear, safe message to the client.
Authentication for AI Features
A public AI endpoint can become expensive very quickly. If every anonymous visitor can send unlimited requests, automated clients may consume the application's entire AI budget.
Request
↓
Authenticate user
↓
Check permission
↓
Check quota
↓
Validate input
↓
Call AI providerAuthentication is particularly useful when AI requests are associated with user accounts, paid plans, credits, private conversations, or usage limits.
Rate Limiting
Rate limiting prevents a single client from making an unreasonable number of AI requests. It should be enforced on the server rather than relying on React state.
- Limit requests per IP when appropriate.
- Limit requests per authenticated user.
- Limit generation frequency.
- Limit prompt size.
- Limit maximum output.
- Use stricter limits for expensive models.
Tracking AI Usage and Costs
AI introduces a variable cost into an application. Track usage from the beginning if the feature will be publicly available or eventually monetized.
| Metric | Why track it |
|---|---|
| Request count | Understand feature usage |
| Input tokens | Measure context consumption |
| Output tokens | Measure generation |
| Latency | Monitor responsiveness |
| Errors | Monitor reliability |
| Model | Compare model usage |
| Estimated cost | Control spending |
If the application uses credits, quotas, or paid plans, usage calculations should be performed on the server. The browser should never be trusted to tell the backend how much a request cost.
Using Structured AI Output
Not every AI feature should return free-form text. If React needs predictable fields, ask the model for structured output and validate the result on the server.
{
"title": "HTTP Caching",
"summary": "A short explanation of caching.",
"difficulty": "intermediate",
"tags": ["http", "web", "performance"]
}Structured responses make AI easier to integrate with normal application logic. For example, a React interface can render a title, summary, tags, and difficulty independently instead of parsing arbitrary generated prose.
Prompt Management
As an AI application grows, prompts can become difficult to maintain if they are scattered throughout React components. Keep important prompt templates in server-side modules or another controlled location.
export function buildPrompt(topic: string) {
return `
You are a technical writing assistant.
Explain the following topic clearly and accurately.
Topic:
${topic}
Requirements:
- Use clear language.
- Avoid unnecessary repetition.
- Do not invent facts.
`;
}Keeping prompt construction on the server also makes it harder for ordinary client-side code to accidentally expose internal application instructions.
Protecting Against Prompt Injection
User input should be treated as untrusted data. A user can deliberately provide instructions that attempt to override the application's intended behavior.
Prompt structure can help separate application instructions from user content, but prompts are not a substitute for authorization. If an AI feature can access private data, call external services, modify records, or perform other sensitive operations, those permissions must be enforced by normal application code.
Trusted application rules
+
Untrusted user input
↓
AI model
↓
Untrusted output
↓
Application validationDo Not Automatically Trust Model Output
AI-generated content should be treated as untrusted data. This is especially important when output is used to generate HTML, database operations, file contents, commands, or executable code.
- Escape user-visible HTML where necessary.
- Validate structured output.
- Do not execute arbitrary generated code.
- Do not use model output as an authorization decision.
- Validate external actions before execution.
- Apply normal security controls around generated data.
Creating an AI Service Layer
When a project contains multiple AI features, provider-specific code should not be duplicated across endpoints. A server-side AI service layer can centralize model calls and configuration.
export async function generateText(options: {
prompt: string;
model?: string;
}) {
// Provider-specific implementation
}The rest of the application can call this service without knowing the provider's HTTP format. This makes it easier to switch models or providers later.
Using Multiple Models
Different AI tasks can require different levels of capability. A simple classification task may work well with a fast and inexpensive model, while complex coding or reasoning may benefit from a stronger model.
AI service
│
├── Fast model → simple tasks
├── Strong model → complex tasks
└── Specialized model → specific workloadsKeeping model selection on the server prevents the browser from arbitrarily selecting an expensive model and makes future routing strategies easier to implement.
Adding a Database
A database becomes useful when the application needs to persist conversations, users, generated content, documents, usage information, credits, or other state.
React
↓
Backend
├── AI provider
└── DatabaseFor example, an AI chat application might store conversations and messages in a database while keeping provider credentials and AI requests entirely server-side.
Caching AI Results
Caching can reduce latency and API costs when identical or equivalent requests occur repeatedly. It is most useful for deterministic or reusable operations.
- Cache repeated transformations when appropriate.
- Cache reusable AI results.
- Cache expensive retrieval operations.
- Define expiration rules.
- Invalidate stale results when source data changes.
Highly personalized or intentionally variable generation may not benefit from traditional response caching. The cache strategy should match the application's behavior.
Performance Considerations
The AI provider often becomes the slowest component of an AI-powered application. Latency can depend on model choice, prompt size, output length, network conditions, retrieval operations, and the number of model calls.
- Choose a model appropriate for the task.
- Keep prompts concise.
- Avoid unnecessary context.
- Use streaming for long responses.
- Cache reusable results.
- Avoid unnecessary model calls.
- Measure actual latency.
Cost Optimization
A React application may have very low frontend infrastructure costs while its AI feature generates significant variable expenses. Cost controls should therefore be designed before the feature receives substantial traffic.
- Limit input size.
- Limit output size.
- Use less expensive models for simple tasks.
- Avoid sending unnecessary conversation history.
- Cache reusable responses.
- Limit repeated generations.
- Track usage by user.
- Introduce quotas or credits when appropriate.
Testing AI Features
AI applications require both conventional software testing and quality evaluation. Exact model wording can change between requests, so tests should focus on behavior and properties that matter to the application.
- Invalid requests are rejected.
- Authentication works correctly.
- Rate limits are enforced.
- API keys remain server-side.
- Structured output passes schema validation.
- Provider errors are handled.
- Maximum input and output limits work.
- Representative prompts produce acceptable results.
- Sensitive operations cannot be triggered solely by model output.
Maintain a small set of realistic evaluation prompts and run them whenever prompts, models, or important application logic change. This helps identify quality regressions that ordinary unit tests may not catch.
A Production React AI Architecture
A production application can remain conceptually simple even when several security and reliability layers are added.
React UI
↓
Backend API
├── Authentication
├── Rate limit
├── Validation
↓
AI service
├── Database
├── AI provider
↓
Output validation
↓
React UIA small application does not need every component immediately. The architecture can start with a basic backend endpoint and grow as authentication, persistence, monetization, and traffic requirements appear.
Common Mistakes
- Putting an AI API key in React code.
- Calling the AI provider directly from the browser.
- Relying only on client-side validation.
- Allowing unlimited public AI requests.
- Trusting user-controlled model parameters.
- Ignoring provider errors and timeouts.
- Sending unnecessary context with every request.
- Using expensive models for simple tasks.
- Failing to track usage and costs.
- Trusting arbitrary model output.
- Duplicating provider-specific logic across components.
- Executing generated code without appropriate isolation.
Best Practices Checklist
- Keep private AI credentials on the server.
- Use a backend between React and the AI provider.
- Validate requests on the server.
- Keep authentication and authorization server-side.
- Use rate limits for public AI endpoints.
- Limit input and output sizes.
- Use structured output when predictable data is required.
- Validate AI-generated data before using it.
- Use streaming for long responses when appropriate.
- Track usage, latency, errors, and costs.
- Keep provider logic in a dedicated service layer.
- Choose models according to the task.
- Use realistic evaluation prompts.
- Never treat the AI model as a security boundary.
Frequently Asked Questions
Can I add AI to a React application without a backend?
A backend or other trusted server-side environment is generally recommended when the AI provider requires a private API key. The server protects credentials and provides a place for validation, authentication, rate limiting, quotas, and other business logic.
Can React call an AI API directly?
Technically, a browser can make HTTP requests to an AI API, but exposing a private provider API key in client-side code is unsafe. A production application should normally route the request through a server-side endpoint.
Where should an AI API key be stored?
Store it in a server-side environment variable or another secure secret-management system. It should never be included in the React production bundle or exposed through a public client-side environment variable.
How can I stream AI responses in React?
The backend can return a streaming HTTP response while the AI provider generates content. React can read the response stream and progressively append incoming chunks to the interface. The exact implementation depends on the backend and provider.
How do I prevent users from spending too much on AI requests?
Use server-side rate limiting, authentication, quotas, input and output limits, appropriate model selection, caching, and usage tracking. If users have paid access, enforce credits or quotas on the server before making the provider request.
Conclusion
Adding AI to a React application is primarily an integration and architecture problem rather than a React-specific problem. React provides the interactive interface, while a server-side layer should normally handle communication with the AI provider.
The basic workflow is straightforward: collect input in React, send it to your backend, validate the request, construct the AI request, call the provider, validate the result, and return application data to the frontend. Streaming can make long generations feel more responsive, while structured output can make AI responses easier to integrate with normal application logic.
For production applications, security and cost control are just as important as the model itself. Keep API keys private, authenticate users when necessary, enforce rate limits, track usage, validate model output, and never rely on an AI model to enforce application permissions.
Starting small is usually the best approach. One React component, one backend endpoint, and one AI feature are enough to establish the basic architecture. Authentication, databases, streaming, multiple models, quotas, caching, and other capabilities can then be added as the application grows.