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.
Install one package.
npm install https://folio.atef.dev/releases/ionite-folio-storage-0.1.0-rc.2.tgzNode.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 suffix | Use it in | What you get |
|---|---|---|
| /server | Your server | Typed uploads, files, tenant quotas and usage. Keep the workspace key here. |
| /next | Your app routes | An adapter that applies your authentication and file policy on every request. |
| /browser | Your browser | Call your authorized app routes. No workspace key in the browser. |
| /react | Your React app | Provider, upload and file hooks, pagination, processing state and temporary links. |
| /ui | Optional components | Uploader, library, preview and file details with customization hooks. |
| /styles.css | Optional styles | Default 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.
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.
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.
'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:
npx [email protected] add https://folio.atef.dev/r/0.1.0-rc.2/folio-files.json'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.