All-in-one React package for AI streaming, events, flows, and generative UI with tree-shaking support
npm install @inference-ui/react

React hooks for AI streaming, chat, completions, and generative UI. Built on Cloudflare Workers AI for ultra-low latency edge inference.
ā ļø Package Renamed: This package was previously published as inference-ui-react. Please update your dependencies to @inference-ui/react.
- š Streaming AI Responses - Real-time token streaming for instant UX
- š¬ Chat Interface - Multi-turn conversations with message history
- āļø Text Completion - Single-turn completions for autocomplete, summarization
- šÆ Type-Safe Objects - Generate structured data with Zod schema validation
- š§© Generative UI - Render custom React components from AI tool calls
- šØ AI-Powered Components - Pre-built forms, inputs, chat, and search with AI capabilities
- ā” Edge Inference - Powered by Cloudflare Workers AI (180+ locations)
- š Privacy-First - Works with local TFLite models or edge AI
- š¦ Zero Config - Works out of the box with sensible defaults
``bash`
npm install @inference-ui/react zod react
Links:
- npm: https://www.npmjs.com/package/@inference-ui/react
- GitHub: https://github.com/Neureus/Inference-UI
- Documentation: https://inference-ui.dev
The easiest way to get started is with InferenceUIProvider. Wrap your app once and all hooks work automatically:
`tsx
import { InferenceUIProvider, useChat } from '@inference-ui/react';
function App() {
return (
);
}
function ChatDemo() {
// No need to specify 'api' - uses default SaaS endpoint
const { messages, input, setInput, append, isLoading } = useChat();
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (input.trim()) {
append({ role: 'user', content: input });
setInput('');
}
};
return (
$3
For production apps, use environment variables:
`tsx
import { InferenceUIProvider } from '@inference-ui/react';function App() {
return (
config={{
apiUrl: process.env.NEXT_PUBLIC_INFERENCE_API_URL,
apiKey: process.env.NEXT_PUBLIC_INFERENCE_API_KEY, // For paid tiers
experimental_throttle: 50,
}}
>
);
}
`$3
Deploy your own Cloudflare Workers:
`tsx
config={{
apiUrl: 'https://my-inference.company.com',
headers: {
'X-Company-Id': 'your-company',
},
}}
>
`$3
You can still specify
api prop to override the provider config:`tsx
function CustomChat() {
// Override provider for this component only
const { messages, append } = useChat({
api: 'https://custom-endpoint.com/chat',
}); // ... rest of component
}
`Hook Examples
The following examples show individual hook usage. When using
InferenceUIProvider, the api prop is optional.$3
`tsx
import { useCompletion } from '@inference-ui/react';function CompletionDemo() {
const { completion, complete, isLoading } = useCompletion({
api: 'https://inference-ui-api.neureus.workers.dev/stream/completion',
});
return (
onClick={() => complete('Write a haiku about coding')}
disabled={isLoading}
>
Generate Haiku
{completion}
);
}
`$3
`tsx
import { useObject } from '@inference-ui/react';
import { z } from 'zod';const RecipeSchema = z.object({
name: z.string(),
ingredients: z.array(z.string()),
instructions: z.array(z.string()),
prepTime: z.number(),
});
function RecipeGenerator() {
const { object, submit, isLoading } = useObject({
api: 'https://inference-ui-api.neureus.workers.dev/stream/object',
schema: RecipeSchema,
});
return (
onClick={() => submit('Generate a recipe for chocolate chip cookies')}
disabled={isLoading}
>
Generate Recipe
{object && (
{object.name}
Ingredients:
{object.ingredients?.map((ing, i) => (
- {ing}
))}
Instructions:
{object.instructions?.map((step, i) => (
- {step}
))}
{object.prepTime && Prep time: {object.prepTime} minutes
}
)}
);
}
`API Reference
$3
Global configuration provider for all hooks. Wrap your app to eliminate repetitive
api props.#### Props
`typescript
interface InferenceUIConfig {
apiUrl?: string; // Base API URL (default: 'https://inference-ui-api.neureus.workers.dev')
apiKey?: string; // API key for authenticated requests (default: undefined)
headers?: Record; // Default headers for all requests
credentials?: RequestCredentials; // Request credentials mode (default: 'same-origin')
experimental_throttle?: number; // Default throttle time for streaming (ms)
endpoints?: { // Custom endpoint overrides
chat?: string;
completion?: string;
object?: string;
};
}
`#### Examples
Zero-config (free SaaS):
`tsx
`Production setup:
`tsx
config={{
apiUrl: process.env.NEXT_PUBLIC_INFERENCE_API_URL,
apiKey: process.env.NEXT_PUBLIC_INFERENCE_API_KEY,
experimental_throttle: 50,
}}
>
`Self-hosted:
`tsx
config={{
apiUrl: 'https://my-workers.company.com',
endpoints: {
chat: 'https://my-workers.company.com/api/chat',
completion: 'https://my-workers.company.com/api/complete',
},
}}
>
`$3
Hook for building conversational AI interfaces with streaming responses.
#### Parameters
`typescript
interface ChatConfig {
api?: string; // Optional: API endpoint (uses provider if not specified)
id?: string; // Optional: Chat session ID
initialMessages?: UIMessage[]; // Optional: Initial conversation history
maxMessages?: number; // Optional: Limit conversation length
headers?: HeadersConfig; // Optional: Custom headers
body?: BodyConfig; // Optional: Additional request data
credentials?: RequestCredentials; // Optional: CORS credentials
onFinish?: (message: UIMessage) => void; // Optional: Callback on completion
onError?: (error: Error) => void; // Optional: Error handler
experimental_throttle?: number; // Optional: Throttle updates (ms)
}
`#### Return Value
`typescript
interface UseChatResult {
messages: UIMessage[]; // Conversation history
input: string; // Current input value
setInput: (input: string) => void; // Update input
status: StreamStatus; // 'ready' | 'streaming' | 'submitted' | 'error'
error: Error | null; // Current error
append: (message) => Promise; // Send message
reload: () => Promise; // Regenerate last response
stop: () => void; // Stop streaming
isLoading: boolean; // Is currently processing
data?: unknown; // Metadata from response
}
`#### Example with Advanced Features
`tsx
import { useChat } from '@inference-ui/react';
import { MessageList } from '@inference-ui/react';function AdvancedChat() {
const {
messages,
input,
setInput,
append,
reload,
stop,
isLoading,
error,
} = useChat({
api: '/stream/chat',
initialMessages: [
{
id: '1',
role: 'system',
parts: [{ type: 'text', text: 'You are a helpful assistant.' }],
createdAt: new Date(),
},
],
maxMessages: 20,
headers: {
'Authorization': 'Bearer your-token',
},
onFinish: (message) => {
console.log('Assistant responded:', message);
},
onError: (error) => {
console.error('Chat error:', error);
},
});
return (
{/ Use built-in MessageList component /}
{error &&
{error.message}}
);
}
`$3
Hook for single-turn text completions with streaming.
#### Parameters
`typescript
interface CompletionConfig {
api?: string; // Optional: API endpoint (uses provider if not specified)
id?: string; // Optional: Completion ID
initialInput?: string; // Optional: Initial input
initialCompletion?: string; // Optional: Initial completion
headers?: HeadersConfig; // Optional: Custom headers
body?: BodyConfig; // Optional: Additional request data
credentials?: RequestCredentials; // Optional: CORS credentials
onFinish?: (completion: string) => void; // Optional: Callback on completion
onError?: (error: Error) => void; // Optional: Error handler
experimental_throttle?: number; // Optional: Throttle updates (ms)
}
`#### Return Value
`typescript
interface UseCompletionResult {
completion: string; // Current completion text
input: string; // Current input value
setInput: (input: string) => void; // Update input
status: StreamStatus; // Current status
error: Error | null; // Current error
complete: (prompt: string) => Promise; // Generate completion
reload: () => Promise; // Regenerate last completion
stop: () => void; // Stop streaming
isLoading: boolean; // Is currently processing
data?: unknown; // Metadata from response
}
`#### Example: Autocomplete
`tsx
import { useCompletion } from '@inference-ui/react';function Autocomplete() {
const { completion, complete, isLoading } = useCompletion({
api: '/stream/completion',
experimental_throttle: 50, // Update every 50ms
});
const [text, setText] = useState('');
const handleChange = (e: React.ChangeEvent) => {
const value = e.target.value;
setText(value);
// Trigger autocomplete after 2 seconds of no typing
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
complete(
Continue this text: ${value});
}, 2000);
}; return (
value={text}
onChange={handleChange}
placeholder="Start typing..."
/>
{isLoading && {completion}}
);
}
`$3
Hook for type-safe structured data generation with Zod validation.
#### Parameters
`typescript
interface ObjectConfig {
api?: string; // Optional: API endpoint (uses provider if not specified)
schema: T; // Required: Zod schema
id?: string; // Optional: Object generation ID
initialValue?: z.infer; // Optional: Initial object
headers?: HeadersConfig; // Optional: Custom headers
body?: BodyConfig; // Optional: Additional request data
credentials?: RequestCredentials; // Optional: CORS credentials
onFinish?: (object: z.infer) => void; // Optional: Callback on completion
onError?: (error: Error) => void; // Optional: Error handler
experimental_throttle?: number; // Optional: Throttle updates (ms)
}
`#### Return Value
`typescript
interface UseObjectResult {
object: Partial | undefined; // Current object (partial during streaming)
input: string; // Current input value
setInput: (input: string) => void; // Update input
status: StreamStatus; // Current status
error: Error | null; // Current error
validationError: z.ZodError | null; // Validation errors
submit: (prompt: string) => Promise; // Generate object
reload: () => Promise; // Regenerate last object
stop: () => void; // Stop streaming
isLoading: boolean; // Is currently processing
isValidating: boolean; // Has validation errors
data?: unknown; // Metadata from response
}
`#### Example: Form Data Generation
`tsx
import { useObject } from '@inference-ui/react';
import { z } from 'zod';const UserProfileSchema = z.object({
name: z.string().min(1),
email: z.string().email(),
age: z.number().min(18).max(120),
interests: z.array(z.string()),
bio: z.string().optional(),
});
function ProfileGenerator() {
const {
object,
submit,
validationError,
isLoading,
isValidating,
} = useObject({
api: '/stream/object',
schema: UserProfileSchema,
onFinish: (profile) => {
console.log('Generated profile:', profile);
// Save to database
},
});
return (
onClick={() => submit('Generate a profile for a software engineer who loves hiking')}
disabled={isLoading}
>
Generate Profile
{object && (
)} {validationError && (
{validationError.errors.map((err, i) => (
{err.path.join('.')}: {err.message}
))}
)}
);
}
`Generative UI
Render custom React components from AI tool calls.
$3
`tsx
import { ToolRegistry } from '@inference-ui/react';
import { z } from 'zod';// Define a tool
const weatherTool = {
name: 'getWeather',
description: 'Get weather for a location',
parameters: z.object({
location: z.string(),
unit: z.enum(['celsius', 'fahrenheit']).optional(),
}),
execute: async (args) => {
const response = await fetch(
/api/weather?location=${args.location});
return response.json();
},
renderComponent: (result, state) => {
if (state === 'output-error') {
return Failed to get weather;
} return (
{result.location}
{result.temperature}°
{result.conditions}
);
},
};// Register tool
ToolRegistry.register(weatherTool);
`$3
`tsx
import { MessageList, ToolRenderer } from '@inference-ui/react';function ChatWithTools() {
const { messages } = useChat({
api: '/stream/chat',
body: {
tools: ToolRegistry.getDefinitions(), // Send tool definitions to API
},
});
return (
messages={messages}
showMetadata={false}
className="chat-messages"
/>
);
}
`Advanced Usage
$3
`tsx
const { messages, append } = useChat({
api: '/stream/chat',
headers: async () => {
const token = await getAuthToken();
return {
'Authorization': Bearer ${token},
'X-User-ID': userId,
};
},
});
`$3
`tsx
const { completion, complete } = useCompletion({
api: '/stream/completion',
body: {
model: 'llama-3.1-70b',
temperature: 0.7,
maxTokens: 500,
},
});
`$3
`tsx
const { completion, isLoading, status } = useCompletion({
api: '/stream/completion',
});return (
{status === 'submitted' && }
{status === 'streaming' && ā}
{status === 'error' && Failed}
{completion}
);
`$3
`tsx
const { error, reload } = useChat({
api: '/stream/chat',
onError: (error) => {
// Log to error tracking service
trackError(error);
// Show user-friendly message
toast.error('Failed to send message. Please try again.');
},
});// Manual retry
if (error) {
return (
Something went wrong: {error.message}
);
}
`AI-Powered Components
Pre-built production-ready components with AI capabilities built-in.
$3
AI-powered form component with real-time validation using Zod schemas.
#### Features
- Real-time AI validation using streaming
- Automatic error detection and helpful messages
- Full TypeScript support with Zod schemas
- Progressive validation on blur
- AI-powered suggestions for field improvements
#### Props
`typescript
interface AIFormProps {
fields: AIFormField[];
schema: T;
onSubmit: (data: z.infer) => void | Promise;
submitLabel?: string;
aiAssisted?: boolean;
className?: string;
children?: React.ReactNode;
}interface AIFormField {
name: string;
label: string;
type?: 'text' | 'email' | 'password' | 'textarea' | 'number';
placeholder?: string;
required?: boolean;
aiValidation?: boolean;
autoComplete?: string;
}
`#### Example
`tsx
import { AIForm } from '@inference-ui/react';
import { z } from 'zod';const UserSchema = z.object({
email: z.string().email(),
username: z.string().min(3),
bio: z.string().max(500),
});
function SignupForm() {
return (
schema={UserSchema}
fields={[
{ name: 'email', label: 'Email', type: 'email', required: true },
{ name: 'username', label: 'Username', required: true },
{ name: 'bio', label: 'Bio', type: 'textarea', aiValidation: true },
]}
onSubmit={(data) => {
console.log('Validated data:', data);
// Submit to your API
}}
aiAssisted
submitLabel="Create Account"
/>
);
}
`$3
Intelligent input component with AI-powered autocomplete and validation.
#### Features
- Real-time autocomplete suggestions
- Smart validation with helpful error messages
- Debounced AI requests for performance
- Keyboard navigation (Arrow keys, Enter, Escape)
- Customizable suggestion rendering
#### Props
`typescript
interface AIInputProps {
value: string;
onChange: (value: string) => void;
onSelect?: (suggestion: string) => void;
placeholder?: string;
type?: 'text' | 'email' | 'url' | 'search';
autocomplete?: boolean;
validate?: boolean;
debounce?: number;
className?: string;
disabled?: boolean;
aiPrompt?: string;
}
`#### Example
`tsx
import { AIInput } from '@inference-ui/react';function EmailInput() {
const [email, setEmail] = useState('');
return (
value={email}
onChange={setEmail}
type="email"
autocomplete
validate
placeholder="Enter your email..."
aiPrompt="Suggest professional email formats"
onSelect={(suggestion) => {
console.log('User selected:', suggestion);
}}
/>
);
}
`$3
Full-featured chat interface with streaming AI responses.
#### Features
- Real-time streaming messages
- Auto-scroll to latest message
- Message timestamps
- Loading states and error handling
- Send/stop controls
- Message regeneration
#### Props
`typescript
interface ChatInterfaceProps {
initialMessages?: UIMessage[];
placeholder?: string;
showTimestamps?: boolean;
className?: string;
}
`#### Example
`tsx
import { ChatInterface } from '@inference-ui/react';function CustomerSupport() {
return (
initialMessages={[
{
id: '1',
role: 'assistant',
parts: [{ type: 'text', text: 'Hello! How can I help you today?' }],
createdAt: new Date(),
},
]}
placeholder="Type your question..."
showTimestamps
className="support-chat"
/>
);
}
`$3
Intelligent search component with AI-generated suggestions.
#### Features
- Real-time AI-powered search suggestions
- Semantic search understanding
- Debounced requests for performance
- Keyboard navigation
- Search history
- Custom result rendering
#### Props
`typescript
interface SearchBoxProps {
onSearch: (query: string) => void | Promise;
onSelect?: (result: SearchResult) => void;
placeholder?: string;
aiSuggestions?: boolean;
debounce?: number;
maxSuggestions?: number;
className?: string;
renderResult?: (result: SearchResult) => React.ReactNode;
}interface SearchResult {
id: string;
title: string;
description?: string;
url?: string;
metadata?: Record;
}
`#### Example
`tsx
import { SearchBox } from '@inference-ui/react';function DocSearch() {
const [results, setResults] = useState([]);
return (
onSearch={async (query) => {
const results = await searchDocs(query);
setResults(results);
}}
onSelect={(result) => {
window.location.href = result.url;
}}
aiSuggestions
placeholder="Search documentation..."
maxSuggestions={5}
/>
);
}
`$3
All components include optional default styles that can be imported:
`tsx
import {
aiFormStyles,
aiInputStyles,
chatInterfaceStyles,
searchBoxStyles,
allComponentStyles, // All styles combined
} from '@inference-ui/react';// Inject into your app
function App() {
return (
<>
>
);
}
// Or import individually
function MyForm() {
return (
<>
>
);
}
`Or use your own custom styles by targeting the component class names:
`css
/ Custom AIForm styles /
.ai-form {
max-width: 800px;
}.ai-form-field {
margin-bottom: 2rem;
}
.ai-form-input {
border: 2px solid #3b82f6;
border-radius: 8px;
}
/ Custom ChatInterface styles /
.chat-interface {
height: 600px;
}
.chat-message {
padding: 1rem;
background: #f9fafb;
}
`Backend Setup
$3
@inference-ui/cloudflare package for backend implementation.Deploy streaming endpoints:
`bash
cd packages/@inference-ui/cloudflare
wrangler deploy
`Available endpoints:
-
POST /stream/chat - Conversational AI
- POST /stream/completion - Text completions
- POST /stream/object - Object generation$3
Implement your own streaming endpoint that returns Server-Sent Events (SSE):
`typescript
// Example Express.js endpoint
app.post('/stream/chat', async (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive'); // Send message-start event
res.write(
data: ${JSON.stringify({); // Stream tokens
for await (const token of aiStream) {
res.write(
data: ${JSON.stringify({);
} // Send done event
res.write(
data: ${JSON.stringify({ type: 'done' })}\n\n);
res.end();
});
`TypeScript
Full TypeScript support with strict type inference:
`typescript
import type {
UIMessage,
MessagePart,
StreamStatus,
ToolDefinition,
} from '@inference-ui/react';// Type-safe tool definition
const myTool: ToolDefinition<{ query: string }, SearchResults> = {
name: 'search',
description: 'Search the web',
parameters: z.object({ query: z.string() }),
execute: async (args) => {
return await searchAPI(args.query);
},
};
// Type inference works automatically
const { object } = useObject({
schema: UserSchema,
api: '/stream/object',
});
// object is typed as Partial>
`Performance
- Edge Inference: <100ms latency globally via Cloudflare Workers AI
- Progressive Streaming: Instant UI feedback as tokens arrive
- Efficient Bundling: Tree-shakable exports, ~15KB gzipped
- Optimized Rendering: React.memo and efficient state updates
- Request Batching: Automatic message batching for efficiency
Examples
/examples` for complete demo applications:- Chat Application - Full-featured chat with message history
- Code Autocomplete - Real-time code suggestions
- Form Generator - Dynamic form generation from schemas
- Recipe Generator - Structured data with validation
- Customer Support Bot - Multi-turn conversations with context
See CONTRIBUTING.md for guidelines.
MIT - see LICENSE