Skip to main content
This guide shows you how to build a file sharing feature within your collaborative application using CollabKit’s Storage module.

What You’ll Build

  • File upload with drag-and-drop support
  • Filterable file gallery (by type, by user)
  • File deletion
  • Image preview for uploaded images

Prerequisites

  • A CollabKit server running with a room and users created
  • @collab-kit/client installed

Step 1: Set Up the Client

import CollabKitClient from '@collab-kit/client';

const client = new CollabKitClient({
  serverUrl: 'https://api.collab-kit.com',
  authToken: '<jwt-token>',
});

await client.connect();
await client.join();

Step 2: Upload Files

The Storage module uploads files via HTTP (not WebSocket):
const fileInput = document.getElementById('file-input') as HTMLInputElement;

fileInput.addEventListener('change', async () => {
  const file = fileInput.files?.[0];
  if (!file) return;

  try {
    const result = await client.storage.upload({ file });
    console.log('Uploaded:', result.key, result.url);
    renderFiles(); // Refresh the file list
  } catch (err) {
    console.error('Upload failed:', err);
  }
});

Step 3: Add Drag-and-Drop

const dropZone = document.getElementById('drop-zone');

dropZone.addEventListener('dragover', (e) => {
  e.preventDefault();
  dropZone.classList.add('drag-over');
});

dropZone.addEventListener('dragleave', () => {
  dropZone.classList.remove('drag-over');
});

dropZone.addEventListener('drop', async (e) => {
  e.preventDefault();
  dropZone.classList.remove('drag-over');

  const files = e.dataTransfer?.files;
  if (!files?.length) return;

  for (const file of files) {
    const result = await client.storage.upload({ file });
    console.log('Uploaded:', result.url);
  }

  renderFiles();
});

Step 4: List and Filter Files

// All files in the room
const allFiles = await client.storage.getAll();

// Only images
const images = await client.storage.getAll({ mimeType: 'image/' });

// Only PDFs
const pdfs = await client.storage.getAll({ mimeType: 'application/pdf' });

// Only my files
const myFiles = await client.storage.getAll({ userId: client.userId });

// Combine filters
const myImages = await client.storage.getAll({
  mimeType: 'image/',
  userId: client.userId,
});
async function renderFiles(filter?: { mimeType?: string; userId?: string }) {
  const files = await client.storage.getAll(filter);
  const gallery = document.getElementById('file-gallery');

  gallery.innerHTML = files.map((file) => {
    const isImage = file.mimeType?.startsWith('image/');
    const sizeKB = (file.size / 1024).toFixed(1);
    const uploader = client.users.all.get(file.uploadedBy)?.name ?? 'Unknown';

    return `
      <div class="file-card">
        ${isImage
          ? `<img src="${file.url}" alt="${file.filename}" class="file-preview" />`
          : `<div class="file-icon">${getFileIcon(file.mimeType)}</div>`
        }
        <div class="file-info">
          <a href="${file.url}" target="_blank" rel="noopener">${file.filename}</a>
          <span>${sizeKB} KB</span>
          <span>By ${uploader}</span>
        </div>
        <button onclick="deleteFile('${file.key}')">Delete</button>
      </div>
    `;
  }).join('');
}

function getFileIcon(mimeType: string | null): string {
  if (!mimeType) return '📄';
  if (mimeType.startsWith('image/')) return '🖼️';
  if (mimeType.startsWith('video/')) return '🎬';
  if (mimeType.startsWith('audio/')) return '🎵';
  if (mimeType === 'application/pdf') return '📑';
  return '📄';
}

Step 6: Delete Files

async function deleteFile(key: string) {
  if (!confirm('Delete this file?')) return;

  await client.storage.delete({ key });
  renderFiles(); // Refresh
}

Step 7: Filter Buttons

const filterBtns = document.querySelectorAll('[data-filter]');

filterBtns.forEach((btn) => {
  btn.addEventListener('click', () => {
    const filter = btn.getAttribute('data-filter');
    switch (filter) {
      case 'all':
        renderFiles();
        break;
      case 'images':
        renderFiles({ mimeType: 'image/' });
        break;
      case 'documents':
        renderFiles({ mimeType: 'application/pdf' });
        break;
      case 'mine':
        renderFiles({ userId: client.userId });
        break;
    }
  });
});

Notify Others with Broadcasts

Since file operations use HTTP (not WebSocket), other users don’t automatically see new uploads. Use broadcasts to notify them:
// After uploading
const result = await client.storage.upload({ file });
client.notifications.broadcast('file-uploaded', {
  key: result.key,
  url: result.url,
  filename: file.name,
  uploadedBy: client.userId,
});

// Listen for new uploads from others
client.notifications.on('file-uploaded', ({ filename, uploadedBy }) => {
  const user = client.users.all.get(uploadedBy);
  showNotification(`${user?.name} uploaded ${filename}`);
  renderFiles(); // Refresh the gallery
});

Next Steps

  • Add file preview modals for images and PDFs
  • Attach files to comments
  • Use stores to track file metadata (tags, descriptions)
  • Set up webhooks to process uploads server-side