Chrome Extension File Handling on ChromeOS: Complete Developer Guide (2026)

AppBooster Team · · 4 min read
File management interface on a Chromebook

File Handling for Chrome Extensions on ChromeOS

ChromeOS 120 introduced a powerful capability: Chrome extensions can now register as file handlers, appearing in the Files app’s “Open with” context menu. This means users can open specific file types directly with your extension — just like native apps.

If your extension works with documents, images, data files, or any custom format, file handling integration makes it feel native on Chromebooks.


How It Works

  1. You declare which file types your extension handles in manifest.json
  2. ChromeOS adds your extension to the “Open with” menu for matching files
  3. When a user selects your extension, Chrome opens your designated HTML page
  4. Your JavaScript receives the file via the LaunchQueue API

Step 1: Configure File Handlers

Add the file_handlers array to your manifest:

{
  "manifest_version": 3,
  "name": "Markdown Viewer",
  "version": "1.0",
  "file_handlers": [
    {
      "action": "/viewer.html",
      "name": "Markdown Viewer",
      "accept": {
        "text/markdown": [".md", ".mdx", ".markdown"]
      }
    },
    {
      "action": "/editor.html",
      "name": "Markdown Editor",
      "accept": {
        "text/markdown": [".md", ".mdx"],
        "text/plain": [".txt"]
      }
    }
  ],
  "background": {
    "service_worker": "service-worker.js"
  }
}

File Handler Properties

PropertyRequiredDescription
actionYesPath to the HTML page that handles the file
nameYesDisplay name in the “Open with” menu
acceptYesObject mapping MIME types to file extensions

You can register multiple handlers for different workflows — one for viewing, another for editing.


Step 2: Process Files with LaunchQueue

When ChromeOS opens your handler page, it passes the file through the LaunchQueue API:

// viewer.js
if ('launchQueue' in window) {
  launchQueue.setConsumer(async (launchParams) => {
    if (!launchParams.files || launchParams.files.length === 0) {
      showEmptyState();
      return;
    }

    for (const fileHandle of launchParams.files) {
      const file = await fileHandle.getFile();
      const content = await file.text();
      renderMarkdown(content, file.name);
    }
  });
} else {
  showError('File handling is not supported in this environment.');
}

Working with FileSystemFileHandle

The launchParams.files array contains FileSystemFileHandle objects, which give you full File System Access API capabilities:

async function processFile(fileHandle) {
  // Read the file
  const file = await fileHandle.getFile();
  const content = await file.text();

  // Get metadata
  console.log(`Name: ${file.name}`);
  console.log(`Size: ${file.size} bytes`);
  console.log(`Type: ${file.type}`);
  console.log(`Modified: ${new Date(file.lastModified)}`);

  return { content, metadata: { name: file.name, size: file.size } };
}

Writing Back to the File

If you have an editor extension, you can write changes back:

async function saveFile(fileHandle, newContent) {
  const writable = await fileHandle.createWritable();
  await writable.write(newContent);
  await writable.close();
}

Complete Example: JSON Viewer Extension

manifest.json

{
  "manifest_version": 3,
  "name": "JSON Viewer",
  "version": "1.0",
  "file_handlers": [
    {
      "action": "/viewer.html",
      "name": "View JSON",
      "accept": {
        "application/json": [".json"],
        "application/ld+json": [".jsonld"]
      }
    }
  ]
}

viewer.html

<!DOCTYPE html>
<html>
<head>
  <style>
    body { font-family: monospace; padding: 20px; background: #1e1e1e; color: #d4d4d4; }
    .filename { color: #569cd6; font-size: 14px; margin-bottom: 12px; }
    .json-key { color: #9cdcfe; }
    .json-string { color: #ce9178; }
    .json-number { color: #b5cea8; }
    .json-boolean { color: #569cd6; }
    pre { white-space: pre-wrap; line-height: 1.5; }
  </style>
</head>
<body>
  <div class="filename" id="filename"></div>
  <pre id="output"></pre>
  <script src="viewer.js"></script>
</body>
</html>

viewer.js

if ('launchQueue' in window) {
  launchQueue.setConsumer(async (launchParams) => {
    if (!launchParams.files?.length) return;

    const fileHandle = launchParams.files[0];
    const file = await fileHandle.getFile();
    const text = await file.text();

    document.getElementById('filename').textContent = file.name;

    try {
      const parsed = JSON.parse(text);
      document.getElementById('output').innerHTML = syntaxHighlight(
        JSON.stringify(parsed, null, 2)
      );
    } catch (e) {
      document.getElementById('output').textContent = `Invalid JSON: ${e.message}`;
    }
  });
}

function syntaxHighlight(json) {
  return json.replace(
    /("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,
    (match) => {
      let cls = 'json-number';
      if (/^"/.test(match)) {
        cls = /:$/.test(match) ? 'json-key' : 'json-string';
      } else if (/true|false/.test(match)) {
        cls = 'json-boolean';
      }
      return `<span class="${cls}">${match}</span>`;
    }
  );
}

Requirements and Limitations

RequirementDetail
ChromeOS version120 or later
Manifest versionV3 only
PermissionsNone required for file handling
File typesAny MIME type and extension combination
Multiple filesSupported — iterate launchParams.files

Known Limitations

  • ChromeOS only — This API is not available on Windows, macOS, or Linux Chrome
  • No drag-and-drop — File handlers only work via the Files app context menu
  • Single consumer — Only one setConsumer call per page

What’s Next

File handling transforms your Chrome extension into a first-class citizen on ChromeOS. If your extension processes any type of file, this integration makes the user experience seamless.

Discover more ways to improve your extension at ExtensionBooster.

Share this article

Build better extensions with free tools

Icon generator, MV3 converter, review exporter, and more — no signup needed.

Related Articles