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

@skycanvasstudio/serve-react

v0.3.0

Published

React uploads, media management, previews, and optimized images for File Server.

Readme

@skycanvasstudio/serve-react

React uploads, file selection, previews and a paginated media library for Serve File Server. Supports React 18.2 and 19, ESM/CommonJS, SSR and Next.js client boundaries. No Tailwind dependency.

npm install @skycanvasstudio/serve-react react react-dom
'use client';
import {
  FileServerProvider, MediaLibrary, createTokenProvider,
} from '@skycanvasstudio/serve-react';
import '@skycanvasstudio/serve-react/styles.css';

// Keep this function stable; the provider stores tokens only in memory.
const tokenProvider = createTokenProvider('/api/file-server/token');

export function Assets() {
  return (
    <FileServerProvider baseUrl="https://files.example" tokenProvider={tokenProvider}>
      <MediaLibrary />
    </FileServerProvider>
  );
}

For a complete Next.js example using only the same two server env variables, see the integration guide. A server page passes the public base URL into the provider; no duplicate public env variable is needed.

Create /api/file-server/token on your application's authenticated backend using @skycanvasstudio/serve-server/adapters/fetch. It must authorize your current user before minting a short-lived token. Never give this package a File Server API key. Tokens are origin-bound and application-scoped. File URLs themselves are public; this is not private or signed content delivery.

Components

| Export | Purpose / key props | | --- | --- | | FileServerProvider | baseUrl, tokenProvider, optional fetch, theme, className; wraps children | | AdvancedImage | asset, alt, dimensions, responsiveWidths, sizes, format/quality, fallback, image callbacks | | FilePicker | Native keyboard-accessible file input, drag/drop and focused paste; onFiles, accept, multiple, maxFiles, maxSizeBytes, onReject | | FileUploader | Picker and queue; parallel (default 3, maximum 6), tags, onUploadComplete, onBatchComplete, picker validation props | | UploadQueue | Controlled items, onCancel, onRetry display | | ImagePicker | Controlled single/multiple value/onChange, existing-image browsing, optional onCrop, disabled | | FilePreview | Image/video/audio/PDF/text/download preview; asset, textPreviewBytes (default 32KB), optional caption tracks | | MediaLibrary | Search/type filtering, pagination, grid/list layouts, preview dialog, controlled value/onChange, multiple, pageSize, canUpload, canDelete, canSelect |

All components accept className. AdvancedImage also has wrapperClassName; picker has inputClassName; uploader has pickerClassName/queueClassName; queue has itemClassName; library has toolbar/grid/item class overrides. AdvancedImage.fit controls CSS object-fit; server resizing preserves aspect ratio. Give images explicit dimensions or an intrinsic asset size to avoid layout shift. Changing the image source resets loading/error state. SVGs bypass raster transforms.

Picker rejections contain file, code, and message; codes are too-many-files, file-too-large, file-type-not-allowed, duplicate-file. Paste is handled within the focused picker, not globally across the page.

Hooks

  • useFileServer() returns {client, getToken}; getToken(true) forces refresh.
  • useFiles(filters?) returns {data, pagination, loading, error, refetch}. Filters include page, limit, search, kind, contentType, tags, sortBy and sortOrder. Old requests are cancelled when filters change or the component unmounts.
  • useDeleteFile() returns {remove, pending, error}. Await remove(id) before hiding a deleted asset.
  • useUpload(options?) returns {items, upload, cancel, retry, uploading}. upload(files) resolves to the batch's final items. Concurrent batches share one concurrency limit per hook instance. Failed/cancelled items can be retried explicitly; active requests are cancelled on unmount. Progress 100% means the server may still be processing; success follows a validated response.
const { items, upload, cancel, retry } = useUpload({ parallel: 3 });
return <>
  <FilePicker multiple onFiles={(files) => { void upload(files); }} />
  <UploadQueue items={items} onCancel={cancel} onRetry={retry} />
</>;

HTTP errors use BrowserFileServerError with status/code/message/details. Expired tokens trigger one forced refresh and one retry. Tokens refresh thirty seconds before expiry, deduplicate concurrent requests, and never use browser persistent storage. Keep custom token providers stable with module scope or useMemo. A changed provider or base URL resets the in-memory cache.

Styling and accessibility

Import the shipped CSS once. Styles use fs- classes and --fs-* variables: background, foreground, muted, muted-foreground, border, primary, primary-foreground, danger and radius. Dark mode follows a .dark ancestor or theme="dark" on the provider. Reduced-motion preferences disable transitions.

The library uses a native modal dialog for focus containment and Escape support; closing restores the previous focus. Delete asks for confirmation and reports errors without hiding the asset. Permission props only affect UI: server token scopes enforce access. Provide caption tracks for your audio/video when available. Text preview is bounded and rendered as escaped text, never injected HTML.

MIT licensed.

S3 delivery (0.2 backend)

Stable Serve media URLs redirect to short-lived signed S3 GET URLs, so stored images, variants, cached transforms, video/audio and PDFs bypass the Serve host for file bytes. New transforms are processed once and saved. Local and active/text documents stay on Serve. Buckets remain private, but application content retains its existing public-by-URL policy. Signed URLs expose object paths and access-key IDs, never secret keys; do not persist or log them. For JavaScript fetch/canvas, configure bucket CORS for your actual application origins, GET/HEAD and Range, exposing Content-Length, Content-Range, Accept-Ranges and ETag. Native media display does not ordinarily need CORS. See the client package.

Upload destination and delivery

Choose a destination per upload with storageLocationId: "local", "default", or an owned S3 connection ID. Omit it to inherit the application's destination, then system defaults. Delivery is configured in Serve independently: Proxy streams through Serve; Direct uses expiring signed S3 URLs. Keep the stable Serve URL in your app. Local storage always streams through Serve.

Discover destinations with useStorageLocations(); pass storageLocationId to useUpload().upload(file, options) or <FileUploader storageLocationId="local" />.