FOLIO SDK · RELEASE CANDIDATE

Files that fit your app.

Add a working file library, use just the hooks, or call the server API. Keep your authentication, tenant IDs and design system.

Preview 0.1.0-rc.2 · direct download

Install the compiled release candidate below. The current package is @ionite/folio-storage; it is not published to npm yet. The proposed npm name @folio/sdk still requires confirmed scope ownership.

Download SDK candidateSee live, preview and upcoming features

Install one package.

Install the release candidate
npm install https://folio.atef.dev/releases/ionite-folio-storage-0.1.0-rc.2.tgz

Node.js 22.12 or newer. React and React DOM 18.3 or 19 are optional peers for the React entries. Server, Next and browser entries have no runtime dependencies. Your app does not need this repository or private workspace packages.

Choose the layers you need.

These are entry points in one installed package. For example, @ionite/folio-storage/ui imports the optional UI from that package; you do not install a separate UI package. The package root remains an alias for the server client.

Import suffixUse it inWhat you get
/serverYour serverTyped uploads, files, tenant quotas and usage. Keep the workspace key here.
/nextYour app routesAn adapter that applies your authentication and file policy on every request.
/browserYour browserCall your authorized app routes. No workspace key in the browser.
/reactYour React appProvider, upload and file hooks, pagination, processing state and temporary links.
/uiOptional componentsUploader, library, preview and file details with customization hooks.
/styles.cssOptional stylesDefault presentation using your semantic theme tokens.

1. Store a file from your server.

Create a workspace key in Folio. Put FOLIO_STORAGE_KEY in your server environment, then choose a tenant after authorizing your user.

Server API
import { createFolioStorage } from '@ionite/folio-storage/server';

const folio = createFolioStorage({
  apiKey: process.env.FOLIO_STORAGE_KEY!,
});
const files = folio.forTenant(authorizedWorkspaceId).files;

const file = await files.upload({
  file: new Blob([invoiceBytes]),
  filename: 'invoice.pdf',
  purpose: 'attachment',
  metadata: { invoiceId: 'your-existing-id' },
});
const page = await files.list({ limit: 50 });
const original = await files.download(file.id);

Keep file IDs in your app records. Preview processing is separate from storing the original. PDFs get a preview; raster images get thumbnail and optimized renditions.

2. Connect your app’s permissions.

app/api/files/[[...path]]/route.ts
import { createFolioStorage } from '@ionite/folio-storage/server';
import { createFolioStorageHandlers } from '@ionite/folio-storage/next';
import { getCurrentSession, canPerformFileAction } from '@/lib/your-app-policy';

const handlers = createFolioStorageHandlers({
  client: createFolioStorage({ apiKey: process.env.FOLIO_STORAGE_KEY! }),
  basePath: '/api/files',
  async authorize({ request, action, fileId }) {
    const session = await getCurrentSession(request);
    if (!session) return null;
    if (!(await canPerformFileAction(session, action, fileId))) return null;
    return {
      tenantId: session.workspaceId,
      allowList: session.permissions.includes('files.list-all'),
    };
  },
});

export const { GET, POST, DELETE } = handlers;
export const runtime = 'nodejs';

The two policy functions above belong to your app. Authorize every action, including upload and usage. Set allowList only when the user may see every file in the tenant; for record-level permissions, provide an authorized listFiles index instead.

Create the adapter once, then read fresh sessions on each request. If your proxy reconstructs an internal origin, set allowedOrigins to your exact canonical public origins. A tenant ID selects files; your app’s session and policy decide who can access them.

3. Add a file library.

Optional supplied React UI
'use client';

import { useMemo } from 'react';
import { createFolioBrowserClient } from '@ionite/folio-storage/browser';
import { FolioProvider } from '@ionite/folio-storage/react';
import { FolioUpload, FolioLibrary } from '@ionite/folio-storage/ui';
import '@ionite/folio-storage/styles.css';

export function Files({ userId, workspaceId, permissionVersion }: {
  userId: string; workspaceId: string; permissionVersion: string;
}) {
  const client = useMemo(() => createFolioBrowserClient({
    endpoint: '/api/files',
    contextKey: JSON.stringify([userId, workspaceId, permissionVersion]),
  }), [userId, workspaceId, permissionVersion]);

  return <FolioProvider client={client}>
    <FolioUpload purpose="attachment" />
    <FolioLibrary />
  </FolioProvider>;
}

Change the context when the signed-in user, workspace or permissions change. Your server still determines the authorized tenant. The library offers grid/table views, previews, original download, retry and guarded deletion.

Customize labels, classes and render callbacks, or use useFolioFiles, useFolioFile, useFolioUpload and useFolioLink from /react with your own components. SDK CSS is optional.

Or copy the components into your app.

Install the SDK candidate first. In a React app configured for shadcn and Tailwind, add the matching versioned registry block:

Editable components via shadcn
npx [email protected] add https://folio.atef.dev/r/0.1.0-rc.2/folio-files.json
Use your copied components
'use client';

import { FolioUpload, FolioLibrary } from '@/components/folio/folio-files';
import { FolioFilesProvider } from '@/components/folio/folio-files-provider';

export function Files({ userId, workspaceId, permissionVersion }: {
  userId: string; workspaceId: string; permissionVersion: string;
}) {
  return <FolioFilesProvider
    endpoint="/api/files"
    contextKey={JSON.stringify([userId, workspaceId, permissionVersion])}
  >
    <FolioUpload purpose="attachment" />
    <FolioLibrary />
  </FolioFilesProvider>;
}

The registry copies file components into your source tree. Customize the markup and styles there; the components use public SDK hooks for uploads, processing state and file access. SDK updates do not replace your copied files. Review SDK versions and any later component changes before adopting them.

Serve files through your own app.

Use client.contentUrl(file.id, 'content') or 'thumbnail' in custom rendering to deliver bytes through your app’s authorized route and existing domain. No paid custom domain is needed. Supplied previews use short-lived Folio links; allow the configured Folio origin in your Content Security Policy’s img-src.

Files are limited to 25 MiB. Metadata is a JSON object up to 16 KiB. Signed links expire after five minutes. Uploads are not automatically retried: after a timeout or cancellation, refresh the file list before deciding to upload again. Your own MCP server can use the server client after applying the same customer permissions.

Get an AI summary of Folio