How Markdown Here Stores and Synchronizes User Options Across Browser Instances

Markdown Here uses a custom OptionsStore abstraction that leverages the browser's chrome.storage.sync API to automatically synchronize user preferences across all signed-in browser instances, with intelligent chunking to handle large values like custom CSS.

The open-source Markdown Here extension (available at adam-p/markdown-here) ensures that your custom settings, styles, and preferences remain consistent whether you're composing emails on your work laptop or home desktop. Understanding how user options are stored and synchronized across different browser instances reveals the sophisticated storage architecture that keeps your markdown experience seamless across devices.

The OptionsStore Architecture

At the heart of Markdown Here's persistence layer lies the OptionsStore class, defined in src/common/options-store.js. This module provides a unified asynchronous API for reading and writing user preferences while abstracting away the complexities of browser storage mechanisms.

Storage Backend and Sync API

The primary storage mechanism utilizes the WebExtension storage API, specifically chrome.storage.sync. When available, this API automatically replicates stored JSON objects to the user's signed-in Chrome or Firefox profile, ensuring that options propagate to every installed copy of the extension without manual intervention.

The store detects sync capability through feature detection, preferring the cloud-backed storage whenever the browser supports it.

Fallback Mechanisms for Offline Support

When chrome.storage.sync is unavailable—such as in older Chromium versions or restricted enterprise environments—OptionsStore gracefully degrades to a localStorage shim. This fallback ensures the extension remains functional offline, though preferences will not synchronize across devices until the sync API becomes available again.

Handling Large Values with Intelligent Chunking

Chrome's sync storage imposes strict per-item size limits (approximately 8,192 bytes per key). To accommodate large user-generated content like custom CSS stylesheets or HTML templates, OptionsStore implements an automatic chunking system.

The Chunking Algorithm

When writing data via OptionsStore.set(), the store checks string lengths against the internal _maxlen() threshold. Values exceeding this limit are split into multiple fragments using the delimiter defined by _div (set to '##').

As implemented in src/common/options-store.js (lines 93-100):

if (typeof(val) !== 'string' || val.length < that._maxlen()) {
  finalobj[key] = val;
} else {
  const pieces = Math.ceil(val.length / that._maxlen());
  for (let i = 0; i < pieces; i++) {
    finalobj[key + that._div + i] = val.substr(i * that._maxlen(), that._maxlen());
  }
}

This creates storage keys like main-css##0, main-css##1, etc., allowing the full CSS content to sync across devices despite individual size constraints.

Reassembling Split Values

During retrieval via OptionsStore.get(), the store detects chunked keys by searching for the _div delimiter. It then reconstructs the original value by joining the fragments in order.

From src/common/options-store.js (lines 55-70):

for (const key in sync) {
  const val = sync[key];
  const divIndex = key.indexOf(that._div);
  if (divIndex < 0) {
    finalobj[key] = val;
  } else {
    const base = key.slice(0, divIndex);
    const part = key.slice(divIndex + that._div.length);
    tempobj[base] = tempobj[base] || [];
    tempobj[base][part] = val;
  }
}
// Join the pieces
for (const key in tempobj) {
  finalobj[key] = tempobj[key].join('');
}

This transparent chunking ensures users can store extensive custom styles without worrying about synchronization limits.

Defaults and Migration Strategy

To maintain consistency across browser instances, OptionsStore implements robust default value handling and legacy migration logic.

Populating Missing Keys

When options are first accessed, the _fillDefaults() method merges the retrieved object with a comprehensive defaults table. This ensures that new installations or newly synced instances receive sensible defaults for all settings, including loading default CSS files via Utils.getLocalFile from src/common/utils.js.

Legacy Value Migration

The storage layer also handles schema evolution. For example, the system automatically migrates legacy Google Chart LaTeX URLs to current endpoints, ensuring that synchronized preferences remain functional even as the extension evolves. This migration runs consistently across all instances, preventing configuration drift.

Cross-Instance Synchronization Flow

The actual synchronization of user options across different browser instances relies on the browser's native cloud sync infrastructure, orchestrated through the extension's background script.

Background Script Integration

The background script (src/chrome/backgroundscript.js) serves as the central authority for option retrieval. It calls OptionsStore.get() whenever current preferences are needed—for example, before rendering markdown or displaying upgrade notifications.

// Retrieve the whole options object (used by many parts of the extension)
// backgroundscript.js → line 68-71
OptionsStore.get(function(prefs) {
  // `prefs` now contains the merged defaults and any user-saved values,
  // identical on every synced browser instance.
  // Example: use the main CSS and syntax CSS together.
  const combinedCss = prefs['main-css'] + prefs['syntax-css'];
});

Because chrome.storage.sync replicates data to the user's profile, every installed copy of Markdown Here receives identical preference objects on startup.

Content Script Communication

Content scripts access these synchronized options via message passing. When a content script requires the latest settings, it sends a "get-options" action through runtime.onMessage. The background script responds with the object returned by OptionsStore.get(), guaranteeing that tabs opened in new windows or on different devices see the synchronized settings immediately.

// Save an option – e.g., user toggles the GFM line-breaks feature
const newOpts = { 'gfm-line-breaks-enabled': true };
OptionsStore.set(newOpts, function() {
  // Callback runs after the data has been written (and split if needed)
  console.log('Option saved and synced!');
});

Summary

  • Markdown Here uses the OptionsStore abstraction in src/common/options-store.js to manage user preferences across browser instances.
  • The system prioritizes chrome.storage.sync for automatic cloud synchronization, falling back to localStorage when the sync API is unavailable.
  • Intelligent chunking splits large values (like custom CSS) across multiple storage keys to bypass Chrome's per-item size limits, then reassembles them transparently on retrieval.
  • Default value filling and legacy migration ensure consistent behavior across all synchronized instances, even as the extension updates.
  • The background script serves as the central authority, responding to content script requests with the latest synchronized options via message passing.

Frequently Asked Questions

How does Markdown Here handle large custom CSS files that exceed browser storage limits?

Markdown Here automatically splits large strings into chunks using the OptionsStore chunking system. When a value exceeds the maximum length returned by _maxlen(), the set() method divides the string into pieces named with the pattern key##0, key##1, etc., using the ## delimiter. Upon retrieval, get() detects these chunked keys and reassembles the full value by joining the fragments in order. This allows even extensive custom stylesheets to synchronize across devices without hitting Chrome's per-item quota.

What happens to my Markdown Here settings when I switch between Chrome and Firefox?

Since OptionsStore relies on the standard WebExtension storage.sync API, your settings synchronize through the browser's respective cloud service (Chrome Sync or Firefox Sync) as long as you are signed into the same account. The extension stores preferences as JSON objects that are platform-agnostic. However, if you use browsers that do not share the same sync infrastructure (e.g., Chrome vs. Safari), the settings will not transfer automatically, though the extension will continue to function using local defaults or localStorage fallback.

How does the extension ensure new browser instances receive the correct default settings?

When OptionsStore.get() is called, it triggers _fillDefaults(), which merges the retrieved storage object with a comprehensive defaults table. This table includes built-in values for all settings, and for CSS-related options, it loads default files via Utils.getLocalFile from src/common/utils.js. If a key is missing from storage (common on new installations), the default value is populated. This ensures that every synchronized instance, whether fresh or existing, maintains consistent baseline behavior even before the user customizes any settings.

Can Markdown Here work offline without losing my preferences?

Yes. While the extension prefers chrome.storage.sync for cross-device synchronization, it includes a robust fallback mechanism for offline scenarios. If the sync API is unavailable or the device is offline, OptionsStore degrades to using a localStorage shim. This allows the extension to read and write preferences locally without interruption. When the browser reconnects and the sync API becomes available again, the storage layer will resume synchronizing changes to the cloud, ensuring your offline modifications propagate to other instances once connectivity is restored.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →