Skip to content

useFileSystem

Saurav-TB-Pandey edited this page Aug 8, 2026 · 1 revision

useFileSystem

The useFileSystem hook provides a powerful "local-first" abstraction over the modern File System Access API. It allows your React application to prompt the user to open a file from their local hard drive, read its content, and continually save edits back to that exact file without prompting them to re-download a new copy every time.

Warning

Browser Compatibility: The File System Access API is currently only supported in Chromium-based browsers (Chrome, Edge, Opera, Brave). Firefox and Safari do not support it due to differing security models. This hook exposes an isSupported flag so you can gracefully degrade your UI.

Security Requirement: This API only works in a Secure Context (HTTPS or localhost).

Usage

Simple Local Text Editor

This example demonstrates how to build a basic Markdown/Text editor that reads from and writes to the user's hard drive directly.

import { useFileSystem } from 'react-hook-lab';

function LocalTextEditor() {
  const { 
    isSupported, 
    status, 
    file, 
    content, 
    error, 
    open, 
    save, 
    saveAs 
  } = useFileSystem({ 
    accept: { 'text/plain': ['.txt', '.md'] },
    description: "Text Files"
  });

  // Graceful degradation for unsupported browsers
  if (!isSupported) {
    return <div>Your browser does not support the File System Access API.</div>;
  }

  return (
    <div>
      <div className="toolbar">
        <button onClick={() => open()} disabled={status === "picking"}>
          Open File
        </button>
        
        {/* If a file is open, save() overwrites it. If no file is open, save() delegates to saveAs()! */}
        <button 
          onClick={() => save(content || "")} 
          disabled={status === "saving"}
        >
          Save
        </button>
        
        <button onClick={() => saveAs(content || "", { suggestedName: "copy.txt" })}>
          Save As...
        </button>
      </div>

      {error && <p style={{ color: "red" }}>Error: {error.message}</p>}

      {status === "reading" && <p>Loading file...</p>}
      
      {file && (
        <div className="editor">
          <h3>Editing: {file.name} (Last modified: {new Date(file.lastModified).toLocaleString()})</h3>
          <textarea 
            value={content || ""}
            onChange={(e) => {
               // Update local state (you'd typically manage this with useState in your component, 
               // but save() takes the new content to write to disk).
            }}
            rows={20}
            cols={80}
          />
        </div>
      )}
    </div>
  );
}

API

Configuration Options (UseFileSystemOptions)

You can pass these options when initializing the hook, or override them when calling open(options) or saveAs(content, options).

  • accept: Record<string, string[]>: A map of MIME types to an array of extensions to filter the file picker. Example: { 'image/png': ['.png'], 'text/html': ['.html', '.htm'] }.
  • description: string: A human-readable description for the file types (e.g., "Images" or "Text Files").
  • suggestedName: string: (Only applicable to saveAs) Suggests a default file name in the Save dialog.

Returns (UseFileSystemReturn)

  • isSupported: boolean: true if window.showOpenFilePicker exists in the current environment.
  • status: "idle" | "picking" | "reading" | "saving" | "error": The current status of the file operation.
  • file: File | null: The standard HTML5 File object containing metadata like name, size, and lastModified.
  • handle: FileSystemFileHandle | null: The underlying handle to the file.
  • content: string | null: The text content of the file (read automatically when opened).
  • error: Error | null: The last error encountered. Note that user cancellations (AbortError) are caught silently and do not trigger an error state.
  • open: (options?: UseFileSystemOptions) => Promise<void>: Opens the native file picker to read a file.
  • save: (newContent: string | Blob, options?: UseFileSystemSaveAsOptions) => Promise<void>: Silently overwrites the currently opened file with newContent. If no file is currently open, it automatically delegates to saveAs() to prompt the user to create a new file.
  • saveAs: (newContent: string | Blob, options?: UseFileSystemSaveAsOptions) => Promise<void>: Prompts the user to pick a new save location and writes the content to it, updating the active handle.
  • reset: () => void: Clears the currently opened file and resets the state to idle.

Clone this wiki locally