Skip to content

Plugable-IO/react

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

@plugable-io/react

React components and hooks for the Plugable File Management API.

Installation

npm install @plugable-io/react

Components & Hooks

Export Type Description
PlugableProvider Component Context provider for configuration
Dropzone Component Drag & drop file uploader
FilePreview Component Universal file preview (images + file type icons)
FileImage Component Image display with caching
FileList Component Paginated file list (render prop)
useFiles Hook File fetching with pagination
usePlugable Hook Access client and context

Quick Start

Provider Setup

Wrap your app with PlugableProvider. You can provide a getToken function or use a supported auth provider.

With Clerk

import { PlugableProvider } from '@plugable-io/react';

function App() {
  return (
    <PlugableProvider
      bucketId="your-bucket-id"
      authProvider="clerk"
      clerkJWTTemplate="plugable" // optional
    >
      <YourApp />
    </PlugableProvider>
  );
}

With Supabase

<PlugableProvider
  bucketId="your-bucket-id"
  authProvider="supabase"
>
  <YourApp />
</PlugableProvider>

With Custom getToken

<PlugableProvider
  bucketId="your-bucket-id"
  getToken={async () => {
    return await getAuthToken();
  }}
>
  <YourApp />
</PlugableProvider>

Dropzone

File uploader with drag-and-drop support. Works with default UI or custom render function.

Default UI

import { Dropzone } from '@plugable-io/react';

function ProfileUploader() {
  return (
    <Dropzone
      metadata={{ category: 'avatar', userId: '123' }}
      accept="image/*"
      maxFiles={1}
      onUploadComplete={(files) => {
        console.log('Avatar uploaded:', files[0]);
      }}
      onUploadError={(error) => {
        console.error('Upload failed:', error);
      }}
    />
  );
}

Custom UI with Render Props

import { Dropzone } from '@plugable-io/react';

function DocumentUploader() {
  return (
    <Dropzone
      metadata={{ type: 'document' }}
      accept=".pdf,.doc,.docx"
      maxFiles={10}
      onUploadComplete={(files) => console.log('Uploaded:', files)}
    >
      {({ isDragActive, isUploading, uploadProgress, openFileDialog, uploadedFiles }) => (
        <div
          onClick={openFileDialog}
          className={`border-2 border-dashed rounded-lg p-8 text-center cursor-pointer
            ${isDragActive ? 'border-blue-500 bg-blue-50' : 'border-gray-300'}`}
        >
          {isUploading ? (
            <div className="space-y-2">
              <p className="font-medium">Uploading...</p>
              {Object.entries(uploadProgress).map(([name, progress]) => (
                <div key={name} className="text-sm">
                  <span>{name}</span>
                  <div className="w-full bg-gray-200 rounded h-2 mt-1">
                    <div
                      className="bg-blue-500 h-2 rounded transition-all"
                      style={{ width: `${progress}%` }}
                    />
                  </div>
                </div>
              ))}
            </div>
          ) : (
            <>
              <p className="font-medium">
                {isDragActive ? 'Drop files here' : 'Drag & drop documents'}
              </p>
              <p className="text-sm text-gray-500 mt-1">or click to browse</p>
            </>
          )}

          {uploadedFiles.length > 0 && (
            <div className="mt-4 text-sm text-green-600">
              βœ“ {uploadedFiles.length} file(s) uploaded
            </div>
          )}
        </div>
      )}
    </Dropzone>
  );
}

Props

Prop Type Description
bucketId string Override default bucket ID
metadata Record<string, any> Metadata to attach to uploaded files
accept string File types to accept (e.g., "image/*", ".pdf,.doc")
maxFiles number Maximum number of files
onUploadComplete (files: FileObject[]) => void Called when uploads complete
onUploadError (error: Error) => void Called on upload error
onProgressUpdate (fileName: string, progress: number) => void Progress callback
children (props: DropzoneRenderProps) => ReactNode Custom render function
className string CSS class name
style CSSProperties Inline styles

useFiles Hook

Fetch and paginate files with automatic refresh on uploads.

Basic Usage

import { useFiles, FilePreview } from '@plugable-io/react';

function InvoiceList() {
  const { files, isLoading, pagination, refresh } = useFiles({
    metadata: { type: 'invoice' },
    perPage: 10,
  });

  if (isLoading) return <div>Loading...</div>;

  return (
    <div>
      <div className="grid grid-cols-4 gap-4">
        {files.map((file) => (
          <div key={file.id} className="border rounded p-3">
            <FilePreview file={file} width={60} height={60} />
            <p className="text-sm mt-2 truncate">{file.name}</p>
            <div className="flex gap-2 mt-2">
              <button onClick={() => file.delete()}>Delete</button>
              <a href={file.download_url} download>Download</a>
            </div>
          </div>
        ))}
      </div>

      <div className="flex gap-2 mt-4">
        <button
          onClick={pagination.loadPreviousPage}
          disabled={!pagination.hasPrevious}
        >
          Previous
        </button>
        <span>Page {pagination.current}</span>
        <button
          onClick={pagination.loadNextPage}
          disabled={!pagination.hasNext}
        >
          Next
        </button>
      </div>
    </div>
  );
}

Filter by Media Type

function ImageGallery() {
  const { files, isLoading } = useFiles({
    mediaType: 'image',
    perPage: 20,
  });

  return (
    <div className="grid grid-cols-3 gap-4">
      {files.map((file) => (
        <FilePreview key={file.id} file={file} width="100%" height={200} objectFit="cover" />
      ))}
    </div>
  );
}

Manual Loading

function LazyFileList() {
  const { files, isLoading, refresh } = useFiles({
    metadata: { folder: 'reports' },
    autoLoad: false, // Don't load on mount
  });

  return (
    <div>
      <button onClick={refresh}>Load Files</button>
      {isLoading && <span>Loading...</span>}
      {files.map((file) => (
        <div key={file.id}>{file.name}</div>
      ))}
    </div>
  );
}

Options

Option Type Default Description
metadata Record<string, any> - Filter files by metadata
mediaType string - Filter by media type (image, video, etc.)
perPage number 20 Files per page
startPage number 1 Initial page number
autoLoad boolean true Fetch on mount

Return Value

Property Type Description
files FileObject[] Array of files
isLoading boolean Loading state
pagination.current number Current page
pagination.hasNext boolean Has next page
pagination.hasPrevious boolean Has previous page
pagination.loadNextPage () => void Go to next page
pagination.loadPreviousPage () => void Go to previous page
setPage (page: number) => void Jump to specific page
refresh () => Promise<void> Refresh current page

FilePreview

Universal file preview component. Displays images inline and shows file type icons for other files.

Basic Usage

import { FilePreview } from '@plugable-io/react';

function FileCard({ file }) {
  return (
    <div className="flex items-center gap-3 p-3 border rounded">
      <FilePreview file={file} width={48} height={48} />
      <div>
        <p className="font-medium">{file.name}</p>
        <p className="text-sm text-gray-500">{file.content_type}</p>
      </div>
    </div>
  );
}

Custom Non-Image Rendering

import { FilePreview } from '@plugable-io/react';

function CustomFilePreview({ file }) {
  return (
    <FilePreview
      file={file}
      width={80}
      height={80}
      objectFit="cover"
      renderNonImage={(file) => (
        <div className="flex flex-col items-center justify-center text-gray-500">
          <span className="text-2xl">πŸ“„</span>
          <span className="text-xs mt-1">{file.name.split('.').pop()}</span>
        </div>
      )}
    />
  );
}

Props

Prop Type Default Description
file FileObject required File to preview
width number | string 80 Preview width
height number | string 80 Preview height
objectFit 'contain' | 'cover' | 'fill' | 'none' | 'scale-down' 'cover' Image fit mode
className string - CSS class name
style CSSProperties - Inline styles
showExtension boolean true Show file extension for non-images
renderNonImage (file: FileObject) => ReactNode - Custom renderer for non-image files

FileImage

Display images with automatic URL fetching and caching.

import { FileImage } from '@plugable-io/react';

function Avatar({ file }) {
  return (
    <FileImage
      file={file}
      width={100}
      height={100}
      objectFit="cover"
      borderRadius={50}
      alt="User avatar"
      onLoad={() => console.log('Loaded')}
    />
  );
}

FileList Component

Render prop component for file listing (alternative to useFiles hook).

import { FileList, FilePreview } from '@plugable-io/react';

function Documents() {
  return (
    <FileList metadata={{ type: 'document' }} perPage={20}>
      {({ files, isLoading, hasMore, loadMore, error }) => (
        <div>
          {error && <p className="text-red-500">Error: {error.message}</p>}
          
          {files.map((file) => (
            <div key={file.id} className="flex items-center gap-3 p-2">
              <FilePreview file={file} width={40} height={40} />
              <span>{file.name}</span>
              <button onClick={() => file.delete()}>Delete</button>
            </div>
          ))}

          {hasMore && (
            <button onClick={loadMore} disabled={isLoading}>
              {isLoading ? 'Loading...' : 'Load More'}
            </button>
          )}
        </div>
      )}
    </FileList>
  );
}

File Object Methods

Each FileObject has built-in methods for updates and deletion:

// Update file metadata
await file.update({
  metadata: { tags: ['important', 'reviewed'] }
});

// Delete file
await file.delete();

// Access properties
console.log(file.id);           // Unique ID
console.log(file.name);         // Filename
console.log(file.content_type); // MIME type
console.log(file.download_url); // Signed download URL
console.log(file.metadata);     // Custom metadata

Full Example

Complete file manager with upload, list, and preview:

import { 
  PlugableProvider, 
  Dropzone, 
  useFiles, 
  FilePreview 
} from '@plugable-io/react';

function FileManager() {
  const { files, isLoading, pagination, refresh } = useFiles({
    perPage: 12,
  });

  return (
    <div className="max-w-4xl mx-auto p-6">
      <h1 className="text-2xl font-bold mb-6">File Manager</h1>

      {/* Upload Section */}
      <Dropzone
        maxFiles={5}
        onUploadComplete={(uploaded) => {
          console.log('Uploaded:', uploaded);
          // Files list auto-refreshes via event system
        }}
        className="mb-8"
      />

      {/* Files Grid */}
      {isLoading ? (
        <div className="text-center py-8">Loading...</div>
      ) : (
        <div className="grid grid-cols-4 gap-4">
          {files.map((file) => (
            <div key={file.id} className="border rounded-lg p-3">
              <FilePreview 
                file={file} 
                width="100%" 
                height={120} 
                objectFit="cover" 
              />
              <p className="text-sm mt-2 truncate" title={file.name}>
                {file.name}
              </p>
              <div className="flex gap-2 mt-2">
                <a 
                  href={file.download_url} 
                  download
                  className="text-blue-500 text-sm"
                >
                  Download
                </a>
                <button 
                  onClick={() => file.delete()}
                  className="text-red-500 text-sm"
                >
                  Delete
                </button>
              </div>
            </div>
          ))}
        </div>
      )}

      {/* Pagination */}
      <div className="flex justify-center gap-4 mt-6">
        <button
          onClick={pagination.loadPreviousPage}
          disabled={!pagination.hasPrevious}
          className="px-4 py-2 border rounded disabled:opacity-50"
        >
          Previous
        </button>
        <span className="py-2">Page {pagination.current}</span>
        <button
          onClick={pagination.loadNextPage}
          disabled={!pagination.hasNext}
          className="px-4 py-2 border rounded disabled:opacity-50"
        >
          Next
        </button>
      </div>
    </div>
  );
}

// App wrapper
function App() {
  return (
    <PlugableProvider
      bucketId="your-bucket-id"
      authProvider="clerk"
    >
      <FileManager />
    </PlugableProvider>
  );
}

TypeScript

All types are exported:

import type {
  PlugableProviderProps,
  AuthProvider,
  DropzoneProps,
  DropzoneRenderProps,
  FilePreviewProps,
  FileImageProps,
  FileListProps,
  FileListRenderProps,
  UseFilesOptions,
  UseFilesResult,
  FileObject,
  SearchOptions,
  UpdateOptions,
} from '@plugable-io/react';

License

MIT

About

React library for Plugable

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published