@easyrag/react-components
v0.1.2
Published
React components for EasyRAG - RAG as a Service (uses Tailwind CSS)
Downloads
269
Maintainers
Readme
@easyrag/react-components
Production-ready React components for EasyRAG - add RAG to your app in minutes.
Installation
npm install @easyrag/react-componentsComponents
- FileUpload - Upload and index documents
- SearchBox - Semantic search interface
- ChatBox - AI chat with streaming responses
- FileViewer - Browse and manage uploaded files
Quick Start
FileUpload
import { FileUpload } from '@easyrag/react-components';
function App() {
return (
<FileUpload
token={yourToken}
datasetId="my-dataset"
onUploadComplete={(result) => {
console.log('Upload complete!', result);
}}
/>
);
}SearchBox
import { SearchBox } from '@easyrag/react-components';
function App() {
return (
<SearchBox
token={yourToken}
datasetId="my-dataset"
onResults={(results) => {
console.log('Search results:', results);
}}
/>
);
}ChatBox
import { ChatBox } from '@easyrag/react-components';
function App() {
return (
<ChatBox
token={yourToken}
datasetId="my-dataset"
stream={true}
/>
);
}FileViewer
import { FileViewer } from '@easyrag/react-components';
function App() {
return (
<FileViewer
token={yourToken}
datasetId="my-dataset"
onFileSelect={(file) => {
console.log('Selected file:', file);
}}
/>
);
}Authentication
Components support two authentication modes:
Mode 1: Frontend Tokens (Recommended)
Generate short-lived tokens from your backend:
// Backend endpoint
app.post('/api/tokens/create', authenticate, async (req, res) => {
const { datasetId } = req.body;
const token = signFrontendToken({
customerId: req.user.id,
datasetId,
ttlSeconds: 3600, // 1 hour
});
res.json({ token });
});
// Frontend usage
const token = await fetch('/api/tokens/create', {
method: 'POST',
body: JSON.stringify({ datasetId: 'my-dataset' })
}).then(r => r.json()).then(d => d.token);
<FileUpload token={token} datasetId="my-dataset" />Mode 2: API Keys (Internal Tools Only)
For internal tools or testing:
<FileUpload
token={process.env.EASYRAG_API_KEY}
datasetId="my-dataset"
/>⚠️ Never expose API keys in production frontends!
Customization
Themes
Three built-in themes:
<SearchBox
token={token}
datasetId="my-dataset"
theme="default" // Default EasyRAG styling
theme="minimal" // Clean, minimal design
theme="custom" // Use customStyles
/>Custom Styles
<SearchBox
token={token}
datasetId="my-dataset"
theme="custom"
customStyles={{
container: 'my-search-container',
input: 'my-custom-input',
button: 'my-custom-button',
resultCard: 'my-result-card',
}}
/>Custom Labels
<ChatBox
token={token}
datasetId="my-dataset"
labels={{
placeholder: 'Ask me anything...',
buttonSend: 'Submit',
emptyState: 'Start chatting!',
}}
/>Powered By Watermark
<FileUpload
token={token}
datasetId="my-dataset"
showPoweredBy={true} // Default: true
poweredByUrl="https://easyrag.com" // Default
/>
// Hide watermark (not recommended)
<FileUpload
showPoweredBy={false}
/>API Reference
FileUpload Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| token | string | required | Auth token |
| datasetId | string | required | Dataset ID |
| apiUrl | string | https://api.easyrag.com/v1 | API base URL |
| metadataBuilder | function | undefined | Build metadata per file |
| maxFiles | number | 10 | Max files per upload |
| maxFileSize | number | undefined | Max file size (bytes) |
| accept | object | undefined | Accepted file types |
| chunkSize | number | undefined | Tokens per chunk |
| chunkOverlap | number | undefined | Overlap between chunks |
| onUploadStart | function | undefined | Called when upload starts |
| onUploadComplete | function | undefined | Called on success |
| onUploadError | function | undefined | Called on error |
| theme | 'default' \| 'minimal' \| 'custom' | 'default' | Visual theme |
| customStyles | object | {} | Custom CSS classes |
| labels | object | {} | Custom text labels |
| showPoweredBy | boolean | true | Show "Powered by EasyRAG" |
SearchBox Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| token | string | required | Auth token |
| datasetId | string | required | Dataset ID |
| apiUrl | string | https://api.easyrag.com/v1 | API base URL |
| filters | array | [] | Metadata filters |
| limit | number | 5 | Results per search |
| maxResults | number | 10 | Max results to display |
| showScore | boolean | true | Show relevance scores |
| showMetadata | boolean | false | Show metadata |
| onSearch | function | undefined | Called on search |
| onResults | function | undefined | Called with results |
| onError | function | undefined | Called on error |
| renderResult | function | undefined | Custom result renderer |
| theme | 'default' \| 'minimal' \| 'custom' | 'default' | Visual theme |
| customStyles | object | {} | Custom CSS classes |
| labels | object | {} | Custom text labels |
| showPoweredBy | boolean | true | Show "Powered by EasyRAG" |
ChatBox Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| token | string | required | Auth token |
| datasetId | string | required | Dataset ID |
| apiUrl | string | https://api.easyrag.com/v1 | API base URL |
| filters | array | [] | Metadata filters |
| stream | boolean | true | Enable streaming |
| maxHeight | string | '400px' | Max chat height |
| initialMessages | array | [] | Pre-populate messages |
| onMessage | function | undefined | Called on new message |
| onError | function | undefined | Called on error |
| onStreamStart | function | undefined | Called when stream starts |
| onStreamEnd | function | undefined | Called when stream ends |
| theme | 'default' \| 'minimal' \| 'custom' | 'default' | Visual theme |
| customStyles | object | {} | Custom CSS classes |
| labels | object | {} | Custom text labels |
| showPoweredBy | boolean | true | Show "Powered by EasyRAG" |
Advanced Examples
Multi-Tenant Upload
<FileUpload
token={token}
datasetId="shared-dataset"
metadataBuilder={(file, index) => ({
userId: currentUser.id,
department: currentUser.department,
uploadedAt: new Date().toISOString(),
originalFileName: file.name,
})}
onUploadComplete={(result) => {
console.log(`Uploaded ${result.files.length} files`);
}}
/>Filtered Search
<SearchBox
token={token}
datasetId="company-docs"
filters={[
{ key: 'department', match: { value: 'engineering' } },
{ key: 'confidential', match: { value: false } },
]}
onResults={(results) => {
console.log(`Found ${results.length} results`);
}}
/>Custom Result Rendering
<SearchBox
token={token}
datasetId="my-dataset"
renderResult={(result, index) => (
<div className="custom-result">
<h3>{result.metadata.title}</h3>
<p>{result.pageContent}</p>
<span>Relevance: {(result.score * 100).toFixed(1)}%</span>
</div>
)}
/>Chat with Context
<ChatBox
token={token}
datasetId="support-docs"
filters={[
{ key: 'category', match: { value: 'billing' } }
]}
initialMessages={[
{
id: 1,
role: 'assistant',
content: 'Hi! I can help with billing questions.',
},
]}
onMessage={(message) => {
// Log messages to analytics
analytics.track('chat_message', {
role: message.role,
length: message.content.length,
});
}}
/>Complete Example
import { useState } from 'react';
import { FileUpload, SearchBox, ChatBox } from '@easyrag/react-components';
function RAGDemo() {
const [token, setToken] = useState('');
const [filesUploaded, setFilesUploaded] = useState(false);
// Get token from your backend
useEffect(() => {
fetch('/api/tokens/create', {
method: 'POST',
body: JSON.stringify({ datasetId: 'my-dataset' })
})
.then(r => r.json())
.then(data => setToken(data.token));
}, []);
if (!token) return <div>Loading...</div>;
return (
<div className="max-w-4xl mx-auto p-6 space-y-8">
<h1>RAG Demo</h1>
{/* Upload */}
<FileUpload
token={token}
datasetId="my-dataset"
onUploadComplete={() => setFilesUploaded(true)}
/>
{/* Search */}
{filesUploaded && (
<SearchBox
token={token}
datasetId="my-dataset"
showScore={true}
/>
)}
{/* Chat */}
{filesUploaded && (
<ChatBox
token={token}
datasetId="my-dataset"
stream={true}
maxHeight="500px"
/>
)}
</div>
);
}TypeScript
Full TypeScript support included:
import type {
FileUploadProps,
SearchBoxProps,
SearchResult,
ChatBoxProps,
Message,
} from '@easyrag/react-components';Styling
Components use semantic class names for easy styling:
/* Target specific components */
.easyrag-file-upload { }
.easyrag-search-box { }
.easyrag-chat-box { }
/* Target specific elements */
.easyrag-file-upload input { }
.easyrag-search-box button { }
.easyrag-chat-box .message-user { }Browser Support
- Chrome/Edge: Latest 2 versions
- Firefox: Latest 2 versions
- Safari: Latest 2 versions
License
MIT
Support
- 📧 Email: [email protected]
- 📖 Docs: easyrag.com/docs
- 🐛 Issues: GitHub Issues
Related Packages
@easyrag/sdk- JavaScript/TypeScript SDK
FileViewer Props
| Prop | Type | Default | Description |
|------|------|---------|-------------|
| token | string | required | Auth token |
| datasetId | string | required | Dataset ID |
| apiUrl | string | https://api.easyrag.com/v1 | API base URL |
| filters | array | [] | Metadata filters |
| files | array | undefined | Pre-loaded files (optional) |
| autoLoad | boolean | true | Auto-load files on mount |
| showSize | boolean | true | Show file sizes |
| showExtension | boolean | true | Show file extensions |
| showMetadata | boolean | false | Show file metadata |
| showTranscription | boolean | true | Show transcriptions |
| showDeleteButton | boolean | true | Show delete button |
| showOpenButton | boolean | true | Show open file button |
| layout | 'split' \| 'list' \| 'grid' | 'split' | Layout mode |
| onFileSelect | function | undefined | Called when file selected |
| onFileDelete | function | undefined | Called when file deleted |
| onFilesLoad | function | undefined | Called when files load |
| onError | function | undefined | Called on error |
| theme | 'default' \| 'minimal' \| 'custom' | 'default' | Visual theme |
| customStyles | object | {} | Custom CSS classes |
| labels | object | {} | Custom text labels |
| showPoweredBy | boolean | true | Show "Powered by EasyRAG" |
File Management with Viewer
import { FileUpload, FileViewer } from '@easyrag/react-components';
function FileManager() {
const [refreshKey, setRefreshKey] = useState(0);
return (
<div className="space-y-6">
{/* Upload new files */}
<FileUpload
token={token}
datasetId="my-dataset"
onUploadComplete={() => {
setRefreshKey(prev => prev + 1); // Refresh viewer
}}
/>
{/* View and manage files */}
<FileViewer
key={refreshKey}
token={token}
datasetId="my-dataset"
layout="split"
showMetadata={true}
showTranscription={true}
onFileDelete={(fileId) => {
console.log('File deleted:', fileId);
}}
/>
</div>
);
}Filtered File Viewer
<FileViewer
token={token}
datasetId="shared-dataset"
filters={[
{ key: 'userId', match: { value: currentUser.id } },
{ key: 'department', match: { value: 'engineering' } }
]}
onFileSelect={(file) => {
console.log('Selected:', file.originalName);
}}
/>List Layout with Custom Styling
<FileViewer
token={token}
datasetId="my-dataset"
layout="list"
theme="custom"
customStyles={{
fileItem: 'p-4 border-b hover:bg-blue-50',
fileItemActive: 'bg-blue-100 border-l-4 border-l-blue-500',
}}
showSize={true}
showExtension={true}
showDeleteButton={false} // Read-only mode
/>