# How to List Files in a Supabase Storage Bucket

- Tool: Supabase
- Difficulty: Intermediate
- Time required: 10-15 min
- Compatibility: Supabase (all plans), @supabase/supabase-js v2+
- Last updated: March 2026

## TL;DR

To list files in a Supabase Storage bucket, use storage.from('bucket-name').list() which returns an array of file and folder objects. You can filter by folder prefix, paginate with limit and offset, sort by name or creation date, and search for specific filenames. The list method respects RLS policies on storage.objects, so users only see files they have SELECT access to.

## Listing and Browsing Files in Supabase Storage Buckets

Supabase Storage organizes files using path prefixes that act as virtual folders. The list() method lets you browse files within a bucket, filter by folder, paginate results, and sort by different criteria. This tutorial shows you how to build a file browser that handles folders, pagination, and search within Supabase Storage.

## Before you start

- A Supabase project with a storage bucket containing files
- The Supabase JS client installed (@supabase/supabase-js v2+)
- RLS policies on storage.objects granting SELECT access
- Basic understanding of async/await in JavaScript

## Step-by-step guide

### 1. List all files in the root of a bucket

Use storage.from('bucket-name').list() to retrieve files at the root level of a bucket. The method returns an array of FileObject items, each containing the file name, id, created_at, updated_at, and metadata. Folders appear as objects with a null id. By default, list() returns up to 100 items from the root level. Files nested in subfolders are not included in root-level listings — you must specify the folder path to list nested files.

```
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)

const { data, error } = await supabase.storage
  .from('documents')
  .list()

if (error) {
  console.error('List error:', error.message)
} else {
  data.forEach((item) => {
    console.log(item.name, item.id ? 'file' : 'folder')
  })
}
```

**Expected result:** An array of file and folder objects at the root level of the bucket is returned.

### 2. List files inside a specific folder

Pass the folder path as the first argument to list() to browse files within that folder. The path should not start or end with a slash. For nested folders, use forward slashes to separate path segments. The returned items are relative to the specified folder, so you only see the immediate children — not deeply nested files.

```
// List files in the 'invoices/2024' folder
const { data, error } = await supabase.storage
  .from('documents')
  .list('invoices/2024')

// List files in a user-specific folder
const userId = 'abc-123-def'
const { data: userFiles, error: userError } = await supabase.storage
  .from('documents')
  .list(userId)
```

**Expected result:** Files and subfolders within the specified path are returned.

### 3. Paginate results with limit and offset

For buckets with many files, use the limit and offset options to paginate results. The limit option sets the maximum number of items returned (default 100), and offset skips that many items from the beginning. Combine these to build a paginated file browser UI. Note that offset-based pagination can miss files if items are added or removed between pages.

```
const PAGE_SIZE = 20
let currentPage = 0

async function loadPage(page: number) {
  const { data, error } = await supabase.storage
    .from('documents')
    .list('uploads', {
      limit: PAGE_SIZE,
      offset: page * PAGE_SIZE,
    })

  if (error) {
    console.error('Pagination error:', error.message)
    return []
  }
  return data
}

// Load first page
const firstPage = await loadPage(0)
// Load second page
const secondPage = await loadPage(1)
```

**Expected result:** Files are returned in pages of the specified size, with offset controlling which page is loaded.

### 4. Sort file listings by column

Use the sortBy option to control the order of returned files. You can sort by 'name', 'created_at', or 'updated_at'. The order can be 'asc' (ascending) or 'desc' (descending). Sorting by created_at in descending order is useful for showing the most recently uploaded files first.

```
// Sort by name ascending (alphabetical)
const { data: byName } = await supabase.storage
  .from('documents')
  .list('uploads', {
    sortBy: { column: 'name', order: 'asc' },
  })

// Sort by creation date, newest first
const { data: byDate } = await supabase.storage
  .from('documents')
  .list('uploads', {
    sortBy: { column: 'created_at', order: 'desc' },
  })
```

**Expected result:** Files are returned in the specified sort order.

### 5. Search for files by name

Use the search option to filter files by name. The search matches files whose name starts with the given string (prefix match). This is useful for implementing a search bar in your file browser. The search is case-sensitive and only matches file names, not folder paths.

```
// Search for files starting with 'report'
const { data, error } = await supabase.storage
  .from('documents')
  .list('uploads', {
    search: 'report',
  })

console.log(`Found ${data?.length} files matching 'report'`)
```

**Expected result:** Only files whose names start with the search string are returned.

## Complete code example

File: `storage-file-browser.ts`

```typescript
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)

interface ListOptions {
  folder?: string
  page?: number
  pageSize?: number
  sortBy?: 'name' | 'created_at' | 'updated_at'
  sortOrder?: 'asc' | 'desc'
  search?: string
}

async function listFiles(bucket: string, options: ListOptions = {}) {
  const {
    folder = '',
    page = 0,
    pageSize = 20,
    sortBy = 'name',
    sortOrder = 'asc',
    search,
  } = options

  const { data, error } = await supabase.storage
    .from(bucket)
    .list(folder, {
      limit: pageSize,
      offset: page * pageSize,
      sortBy: { column: sortBy, order: sortOrder },
      ...(search ? { search } : {}),
    })

  if (error) {
    console.error('List files error:', error.message)
    return { files: [], folders: [], hasMore: false }
  }

  const files = data.filter((item) => item.id !== null)
  const folders = data.filter((item) => item.id === null)
  const hasMore = data.length === pageSize

  return { files, folders, hasMore }
}

// === Usage examples ===

// List root of bucket
const root = await listFiles('documents')
console.log('Root folders:', root.folders.map((f) => f.name))
console.log('Root files:', root.files.map((f) => f.name))

// List a subfolder with pagination
const page1 = await listFiles('documents', {
  folder: 'invoices/2024',
  page: 0,
  pageSize: 10,
  sortBy: 'created_at',
  sortOrder: 'desc',
})
console.log('Page 1 files:', page1.files.map((f) => f.name))
console.log('Has more:', page1.hasMore)
```

## Common mistakes

- **Adding a leading or trailing slash to the folder path** — undefined Fix: The folder path should not start or end with a slash. Use 'invoices/2024' not '/invoices/2024/' — leading and trailing slashes cause the list to return empty results.
- **Expecting deeply nested files to appear when listing a parent folder** — undefined Fix: The list() method only returns immediate children of the specified folder. To see nested files, call list() again with the subfolder path.
- **Forgetting that RLS policies affect list results** — undefined Fix: If your list returns empty but files exist, check that you have a SELECT policy on storage.objects for the authenticated role. Without a SELECT policy, RLS blocks all reads.
- **Using search as a substring match when it is actually a prefix match** — undefined Fix: The search option matches file names that start with the given string. For substring matching, fetch all files and filter client-side with JavaScript's includes() method.

## Best practices

- Always paginate listings with limit and offset for buckets with many files
- Separate files from folders in the response by checking whether the id field is null
- Use the user-scoped folder pattern to list only files belonging to the authenticated user
- Cache file listings on the client and invalidate when files are uploaded or deleted
- Combine sorting with pagination for a consistent file browser experience
- Handle empty results gracefully — display a helpful message instead of a blank page
- Set appropriate RLS SELECT policies on storage.objects before listing files

## Frequently asked questions

### What is the maximum number of files returned by list()?

The default limit is 100 files per call. You can increase this up to 1000 by setting the limit option. For larger listings, use pagination with offset to load files in batches.

### How do I know if a returned item is a folder or a file?

Folders have a null id field and files have a non-null id. Check item.id === null to identify folders in the listing results.

### Can I list files across multiple folders in one call?

No, each list() call retrieves files from a single folder path. To list files across multiple folders, make separate list() calls for each folder and merge the results.

### Why does my file listing return empty even though files exist?

This is almost always an RLS issue. Check that you have a SELECT policy on storage.objects for the authenticated role. Also verify the folder path is correct — no leading or trailing slashes.

### Does the search option support regular expressions?

No, the search option only supports prefix matching. It returns files whose names start with the given string. For more complex search patterns, fetch results and filter them client-side.

### Can I list files in a bucket without authentication?

Only if the bucket is public and you have a SELECT policy for the anon role on storage.objects. For private buckets, the user must be authenticated and have a matching SELECT policy.

### Can RapidDev help build a file management system with Supabase Storage?

Yes, RapidDev can design and implement a complete file management system using Supabase Storage, including folder structures, pagination, search, and user-scoped access controls.

---

Source: https://www.rapidevelopers.com/supabase-tutorial/how-to-list-files-in-supabase-storage-bucket
© RapidDev — https://www.rapidevelopers.com/supabase-tutorial/how-to-list-files-in-supabase-storage-bucket
