The File and Directory Entries API is a web platform feature that lets web apps process directories and file lists that a user provides, typically through drag-and-drop or file/directory picker inputs. Think of it as a practical step beyond the basic File API: instead of dealing with one file at a time, you can work with hierarchies—folders containing files and subfolders—so you can build experiences like “drop a project folder here” or “select a directory to import.”
What it’s for (and what it isn’t)
This API was originally part of a broader effort to support a virtual file system in the browser. Today, its scope is much narrower: it only supports read operations on user-provided data. In other words:
- It’s great for importing data structures (folders, nested assets, batches of files).
- It is not a general-purpose way to browse or modify a user’s disk.
- Access is constrained to what the user explicitly selects or drops.
How you get access to entries
Two common entry points:
- Drag and drop: when handling a drop event, you can obtain a filesystem entry from a dropped item using DataTransferItem.webkitGetAsEntry(). If you get a non-null result, the dropped item is a file or directory, and you can traverse it using filesystem entry calls.
- File inputs (including directory picking):
HTMLInputElement.webkitEntriescan expose selected entries in certain scenarios.- If
HTMLInputElement.webkitdirectoryistrue, the<input>becomes a directory picker, returning directory entries for the chosen folders.
Core concepts and interfaces
At the heart of the API is the idea of an entry—something that can be either a file or a directory:
- FileSystemEntry: the base abstraction for a single entry.
- FileSystemFileEntry: represents a file.
- FileSystemDirectoryEntry: represents a directory.
- FileSystemDirectoryReader: created via FileSystemDirectoryEntry.createReader(), used to read directory contents.
- FileSystem: represents a file system (as a container/namespace for entries).
There are also related extensions exposed elsewhere in the platform, including:
- File.webkitRelativePath, which provides a file’s path relative to the selected directory context.
- The input attributes/properties mentioned above (webkitdirectory, webkitEntries).
When it shines
- File.webkitRelativePath, which provides a file’s path relative to the selected directory context.
- The input attributes/properties mentioned above (webkitdirectory, webkitEntries).
You’ll typically reach for this API when you want to:
- Accept entire folders (e.g., photo directories, static sites, code projects).
- Preserve structure (subfolders and relative paths matter).
- Support drag-and-drop importing in a way that feels natural to users.
File and Directory Entries API Demo
File & Directory Entries API
Drag a folder (with sub-folders) into the box below to see recursion in action.
Drop Files or Folders Here
Waiting for input...
body {
font-family: 'Segoe UI', sans-serif;
padding: 2rem;
max-width: 800px;
margin: 0 auto;
}
/* Visual styling for the drop target */
#drop-zone {
border: 3px dashed #ccc;
border-radius: 10px;
padding: 3rem;
text-align: center;
color: #666;
transition: all 0.3s ease;
background-color: #fafafa;
}
/* Highlight state when dragging over */
#drop-zone.hover {
border-color: #007bff;
background-color: #e9f5ff;
color: #007bff;
}
/* Container for the output log */
#output {
margin-top: 2rem;
background: #f4f4f4;
padding: 1rem;
border-radius: 5px;
font-family: monospace;
white-space: pre-wrap;
min-height: 100px;
}
const dropZone = document.querySelector('#drop-zone');
const output = document.querySelector('#output');
// 1. DEFINE FUNCTIONS FIRST (to avoid ReferenceError)
const preventDefaults = (e) => {
e.preventDefault();
e.stopPropagation();
};
const log = (text) => {
output.textContent += text + '\n';
};
/**
* RECURSIVE FUNCTION
* This creates a tree structure by checking if an item is a File or Directory.
*/
const traverseFileTree = (item, path = '') => {
// The path parameter helps us keep track of the current directory structure as we traverse.
path = path || '';
if (item.isFile) {
// CASE 1: It is a specific File
item.file((file) => {
log(`📄 FILE: ${path}${item.name} (${file.size} bytes)`);
});
} else if (item.isDirectory) {
// CASE 2: It is a Directory
log(`📁 FOLDER: ${path}${item.name}`);
// Create a DirectoryReader to read entries inside this folder
const dirReader = item.createReader();
// readEntries returns an array of entries
dirReader.readEntries((entries) => {
for (let i = 0; i < entries.length; i++) {
// RECURSION: Call this function again for the child entry
traverseFileTree(entries[i], path + item.name + '/');
}
});
}
};
const handleDrop = (e) => {
// Processing visual cue
output.textContent = 'Processing...\n';
// Access the DataTransferItemList interface
const items = e.dataTransfer.items;
for (let i = 0; i < items.length; i++) {
// KEY API METHOD: webkitGetAsEntry()
const entry = items[i].webkitGetAsEntry();
if (entry) {
traverseFileTree(entry);
}
}
};
// 2. ATTACH EVENT LISTENERS (After functions are defined)
// Prevent default behaviors
['dragenter', 'dragover', 'dragleave', 'drop'].forEach((eventName) => {
dropZone.addEventListener(eventName, preventDefaults, false);
});
// Visual cues (Hover effects)
['dragenter', 'dragover'].forEach(() => dropZone.classList.add('hover'));
['dragleave', 'drop'].forEach(() => dropZone.classList.remove('hover'));
// Handle the actual file drop (This was missing in your snippet)
dropZone.addEventListener('drop', handleDrop, false);
Final takeaway
The File and Directory Entries API fills an important niche: it enables structured imports of user-selected files and directories in a way that the basic File API doesn’t. While it doesn’t offer full filesystem access, it’s a strong fit for modern web apps that need to ingest folder-based data—safely, explicitly, and within the boundaries of what the user has shared.

