npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@easyrag/react-components

v0.1.2

Published

React components for EasyRAG - RAG as a Service (uses Tailwind CSS)

Downloads

269

Readme

@easyrag/react-components

Production-ready React components for EasyRAG - add RAG to your app in minutes.

Installation

npm install @easyrag/react-components

Components

  • 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

Related Packages

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
/>