Runtime Support: ChatGPT Apps onlyThis hook is not available in MCP Apps. Calling it in an MCP Apps environment will throw an error.
The useFiles hook provides methods for uploading and downloading files within your widget. Files are managed by the host and can be referenced across tool calls.
Basic usage
import { useFiles } from "skybridge/web";
import { useState } from "react";
function FileUploader() {
const { upload } = useFiles();
const [fileId, setFileId] = useState<string | null>(null);
const handleUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
const { fileId } = await upload(file);
setFileId(fileId);
}
};
return (
<div>
<input type="file" onChange={handleUpload} />
{fileId && <p>Uploaded file ID: {fileId}</p>}
</div>
);
}
Returns
upload
upload: (file: File) => Promise<{ fileId: string }>
Uploads a file to the host. Returns a promise that resolves with the file metadata containing a unique fileId.
file: File
- The file object to upload
getDownloadUrl
getDownloadUrl: (file: { fileId: string }) => Promise<{ downloadUrl: string }>
Get the download URL of a file that was previously uploaded. Returns a promise that resolves with a downloadUrl that can be used to fetch the file content.
file: { fileId: string }
- An object containing the
fileId of the file to download
Only files uploaded by the same connector instance can be downloaded.
Examples
Image Upload and Preview
import { useFiles } from "skybridge/web";
import { useState } from "react";
function ImageUploader() {
const { upload, getDownloadUrl } = useFiles();
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
const [isUploading, setIsUploading] = useState(false);
const handleUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) return;
setIsUploading(true);
try {
const { fileId } = await upload(file);
const { downloadUrl } = await getDownloadUrl({ fileId });
setPreviewUrl(downloadUrl);
} catch (error) {
console.error("Upload failed:", error);
} finally {
setIsUploading(false);
}
};
return (
<div>
<input
type="file"
accept="image/*"
onChange={handleUpload}
disabled={isUploading}
/>
{isUploading && <p>Uploading...</p>}
{previewUrl && <img src={previewUrl} alt="Preview" />}
</div>
);
}
Document Manager
import { useFiles } from "skybridge/web";
import { useState } from "react";
type Document = {
name: string;
fileId: string;
};
function DocumentManager() {
const { upload, getDownloadUrl } = useFiles();
const [documents, setDocuments] = useState<Document[]>([]);
const handleUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) return;
const { fileId } = await upload(file);
setDocuments((prev) => [...prev, { name: file.name, fileId }]);
};
const handleDownload = async (doc: Document) => {
const { downloadUrl } = await getDownloadUrl({ fileId: doc.fileId });
window.open(downloadUrl, "_blank");
};
return (
<div>
<input type="file" onChange={handleUpload} />
<ul>
{documents.map((doc) => (
<li key={doc.fileId}>
{doc.name}
<button onClick={() => handleDownload(doc)}>Download</button>
</li>
))}
</ul>
</div>
);
}
Multiple File Upload
import { useFiles } from "skybridge/web";
import { useState } from "react";
function MultiFileUploader() {
const { upload } = useFiles();
const [uploadedIds, setUploadedIds] = useState<string[]>([]);
const [progress, setProgress] = useState<number>(0);
const handleUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
const files = Array.from(event.target.files || []);
if (files.length === 0) return;
const ids: string[] = [];
for (let i = 0; i < files.length; i++) {
const { fileId } = await upload(files[i]);
ids.push(fileId);
setProgress(((i + 1) / files.length) * 100);
}
setUploadedIds(ids);
setProgress(0);
};
return (
<div>
<input type="file" multiple onChange={handleUpload} />
{progress > 0 && <progress value={progress} max={100} />}
{uploadedIds.length > 0 && (
<p>Uploaded {uploadedIds.length} files</p>
)}
</div>
);
}