Skip to main content

File Browser

Extensions can register custom tabs in Kittl's upload panel, allowing users to browse and select content from external sources (like cloud storage, product catalogs, or media libraries) without leaving the Kittl editor.

Overview

The File Browser API enables extensions to:

  • Add a custom tab to the shared upload panel
  • Display hierarchical content (folders and items)
  • Support pagination for large datasets
  • Provide preview thumbnails for visual content
  • Handle selection and upload of items to the canvas

Manifest Configuration

To use the File Browser API, configure a separate HTML entrypoint in your manifest:

{
"config": {
"embed": {
"sharedFileBrowser": {
"path": "uploadPanel.html"
}
}
}
}

This HTML file should load a script that calls kittl.fileBrowser.registerUploadBrowser() to register your file browser tab.

Basic Implementation

1. Create the Upload Panel HTML

Create a separate HTML file (e.g., uploadPanel.html) that initializes your file browser:

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Upload Panel</title>
</head>
<body>
<script type="module" src="/src/uploadPanel.ts"></script>
</body>
</html>

2. Register Your File Browser

In your upload panel script, call registerUploadBrowser() after SDK initialization:

import { kittl, type FileBrowserTab } from '@kittl/sdk';

kittl.onReady(async () => {
await kittl.fileBrowser.registerUploadBrowser({
id: 'my-extension-browser',
label: 'My Service',
fetchContent: async (params) => {
// Fetch content from your service
const items = await fetchFromMyService(params);

return {
content: items,
next: hasMorePages ? 'next-page-cursor' : undefined,
};
},
});
});

API Reference

registerUploadBrowser(tab: FileBrowserTab)

Registers a custom file browser tab in the upload panel.

Parameters

tab: FileBrowserTab

type FileBrowserTab = {
id: string;
label: string;
fetchContent: (params?: FileBrowserFetchContentParams) => Promise<FileBrowserFetchContentResult>;
};
  • id: Unique identifier for your browser tab
  • label: Display name shown in the tab selector
  • fetchContent: Async function that fetches and returns content

fetchContent Function

Your fetchContent function receives optional parameters and returns a result object:

Input Parameters

type FileBrowserFetchContentParams = {
id?: string; // Folder ID when navigating into a folder
next?: string | number; // Pagination cursor/token
};
  • id: When provided, fetch the contents of the specified folder
  • next: Pagination cursor returned from a previous fetchContent call

Return Value

type FileBrowserFetchContentResult = {
content: FileBrowserContent[];
next?: string | number;
error?: string;
};
  • content: Array of items and/or folders to display
  • next: Optional cursor for the next page (enables "Load More" behavior)
  • error: Optional error message to display to the user

Content Types

Content can be either items (selectable files) or folders (navigable containers):

FileBrowserItem

type FileBrowserItem = {
type: 'item';
id: string;
label: string;
preview?: string;
content: {
data: string; // URL to the actual image/asset
};
};
  • type: Must be 'item'
  • id: Unique identifier for this item
  • label: Display name shown under the preview
  • preview: Optional thumbnail URL
  • content.data: URL to the full-resolution asset

FileBrowserFolder

type FileBrowserFolder = {
type: 'folder';
id: string;
label: string;
preview?: string;
content?: Array<FileBrowserContent>;
};
  • type: Must be 'folder'
  • id: Unique identifier for this folder (passed back as params.id)
  • label: Display name shown under the preview
  • preview: Optional thumbnail URL representing the folder
  • content: Optional pre-fetched child content (rarely used; prefer lazy loading via fetchContent)

Complete Example

Here's a full example showing a product catalog browser with folders and pagination:

import {
kittl,
type FileBrowserContent,
type FileBrowserItem,
type FileBrowserFolder,
} from '@kittl/sdk';

const PAGE_SIZE = 24;

kittl.onReady(async () => {
await kittl.fileBrowser.registerUploadBrowser({
id: 'product-catalog-browser',
label: 'Products',

fetchContent: async (params) => {
const cursor = params?.next ? String(params.next) : undefined;

// Navigating into a specific product folder
if (params?.id) {
const response = await fetch(
`/api/products/${params.id}/images?cursor=${cursor || ''}`
);
const data = await response.json();

const items: FileBrowserItem[] = data.images.map((img) => ({
type: 'item',
id: img.id,
label: img.title || 'Product Image',
preview: img.thumbnailUrl,
content: {
data: img.fullUrl,
},
}));

return {
content: items,
next: data.nextCursor,
};
}

// Root level: show product folders
const response = await fetch(
`/api/products?limit=${PAGE_SIZE}&cursor=${cursor || ''}`
);
const data = await response.json();

const folders: FileBrowserFolder[] = data.products.map((product) => ({
type: 'folder',
id: product.id,
label: product.name,
preview: product.coverImage,
}));

return {
content: folders,
next: data.nextCursor,
};
},
});
});

Best Practices

Performance

  • Lazy load content: Don't pre-fetch folder contents. Let fetchContent handle it when the user navigates.
  • Paginate large datasets: Use the next cursor to implement pagination instead of loading everything at once.
  • Optimize thumbnails: Return appropriately sized preview images (e.g., 200-400px wide) to avoid bandwidth waste.

User Experience

  • Use meaningful labels: Item and folder names should help users identify content at a glance.
  • Provide previews: Visual thumbnails significantly improve the browsing experience.
  • Handle errors gracefully: Return { error: 'User-friendly message' } when fetches fail.
  • Consider empty states: Return an empty array with a helpful error message when no content is available.

Authentication

File browser scripts can access the same authentication methods as your main extension:

kittl.onReady(async () => {
// Check if the user has authenticated with your OAuth provider
const tokenResult = await kittl.auth.getToken('my-provider');

if (!tokenResult.ok) {
// User not authenticated - you may want to show a message
// or skip registering the browser
return;
}

const token = tokenResult.data.accessToken;

await kittl.fileBrowser.registerUploadBrowser({
id: 'my-browser',
label: 'My Service',
fetchContent: async (params) => {
// Use token in API requests
const response = await fetch('/api/files', {
headers: {
Authorization: `Bearer ${token}`,
},
});
// ...
},
});
});

Troubleshooting

File browser tab not appearing

  • Verify sharedFileBrowser.path in your manifest points to the correct HTML file
  • Check browser console for errors during registerUploadBrowser() call
  • Ensure you're calling registerUploadBrowser() inside kittl.onReady()

Images not loading

  • Verify content.data contains a publicly accessible URL
  • Check for CORS issues if images are hosted on a different domain
  • Ensure image URLs use HTTPS

Pagination not working

  • Verify your next cursor is being correctly returned and passed back to fetchContent
  • Check that subsequent calls with the next parameter return new items