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 tablabel: Display name shown in the tab selectorfetchContent: 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 foldernext: Pagination cursor returned from a previousfetchContentcall
Return Value
type FileBrowserFetchContentResult = {
content: FileBrowserContent[];
next?: string | number;
error?: string;
};
content: Array of items and/or folders to displaynext: 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 itemlabel: Display name shown under the previewpreview: Optional thumbnail URLcontent.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 asparams.id)label: Display name shown under the previewpreview: Optional thumbnail URL representing the foldercontent: Optional pre-fetched child content (rarely used; prefer lazy loading viafetchContent)
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
fetchContenthandle it when the user navigates. - Paginate large datasets: Use the
nextcursor 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.pathin your manifest points to the correct HTML file - Check browser console for errors during
registerUploadBrowser()call - Ensure you're calling
registerUploadBrowser()insidekittl.onReady()
Images not loading
- Verify
content.datacontains 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
nextcursor is being correctly returned and passed back tofetchContent - Check that subsequent calls with the
nextparameter return new items
Related Documentation
- App Manifest Reference - Complete manifest schema
- Upload API - Direct upload methods via SDK
- Headless Runners - Background script execution