Production-ready React components for EasyRAG - add RAG to your app in minutes.
npm install @easyrag/react-components- FileUpload - Upload and index documents
- SearchBox - Semantic search interface
- ChatBox - AI chat with streaming responses
- FileViewer - Browse and manage uploaded files
import { FileUpload } from '@easyrag/react-components';
function App() {
return (
<FileUpload
token={yourToken}
datasetId="my-dataset"
onUploadComplete={(result) => {
console.log('Upload complete!', result);
}}
/>
);
}import { SearchBox } from '@easyrag/react-components';
function App() {
return (
<SearchBox
token={yourToken}
datasetId="my-dataset"
onResults={(results) => {
console.log('Search results:', results);
}}
/>
);
}import { ChatBox } from '@easyrag/react-components';
function App() {
return (
<ChatBox
token={yourToken}
datasetId="my-dataset"
stream={true}
/>
);
}import { FileViewer } from '@easyrag/react-components';
function App() {
return (
<FileViewer
token={yourToken}
datasetId="my-dataset"
onFileSelect={(file) => {
console.log('Selected file:', file);
}}
/>
);
}Components support two authentication modes:
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" />For internal tools or testing:
<FileUpload
token={process.env.EASYRAG_API_KEY}
datasetId="my-dataset"
/>Three built-in themes:
<SearchBox
token={token}
datasetId="my-dataset"
theme="default" // Default EasyRAG styling
theme="minimal" // Clean, minimal design
theme="custom" // Use customStyles
/><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',
}}
/><ChatBox
token={token}
datasetId="my-dataset"
labels={{
placeholder: 'Ask me anything...',
buttonSend: 'Submit',
emptyState: 'Start chatting!',
}}
/><FileUpload
token={token}
datasetId="my-dataset"
showPoweredBy={true} // Default: true
poweredByUrl="https://easyrag.com" // Default
/>
// Hide watermark (not recommended)
<FileUpload
showPoweredBy={false}
/>| 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" |
| 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" |
| 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" |
<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`);
}}
/><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`);
}}
/><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>
)}
/><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,
});
}}
/>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>
);
}Full TypeScript support included:
import type {
FileUploadProps,
SearchBoxProps,
SearchResult,
ChatBoxProps,
Message,
} from '@easyrag/react-components';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 { }- Chrome/Edge: Latest 2 versions
- Firefox: Latest 2 versions
- Safari: Latest 2 versions
MIT
- π§ Email: support@easyrag.com
- π Docs: easyrag.com/docs
- π Issues: GitHub Issues
@easyrag/sdk- JavaScript/TypeScript SDK
| 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" |
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>
);
}<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);
}}
/><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
/>