Skip to main content

Overview

The Bookmarks feature provides quick access to your browser’s bookmark tree directly from the Meelio new tab. It syncs with your browser’s native bookmarks and displays them in an organized, visual interface.
Bookmarks requires the optional bookmarks permission. The extension will prompt you to grant this permission when you first access the bookmarks feature.

How It Works

The bookmark system:
  1. Syncs from Browser: Reads your browser’s native bookmark tree
  2. Caches Locally: Stores a local copy in IndexedDB for fast access
  3. Auto-Refreshes: Refreshes cached bookmarks every hour or on manual refresh
  4. Listens for Changes: Automatically updates when you add/edit/delete bookmarks in your browser
From bookmarks.store.ts:39, the cache duration:
const CACHE_DURATION = 60 * 60 * 1000; // 1 hour

Display Modes

From bookmarks.store.ts:9, bookmarks can be displayed in multiple ways:
type BookmarksDisplayMode = 'hidden' | 'bar' | 'sheet' | 'both';
Bookmarks Dynamic IslandA compact widget at the top of your new tab showing recently accessed bookmarks.From newtab.tsx:133:
<BookmarksDynamicIsland />
Features:
  • Shows favorite/recent bookmarks as favicon icons
  • Compact, non-intrusive display
  • Quick one-click access to frequent sites
  • Hovers to show bookmark name
Change display mode from Settings or with:
setDisplayMode('bar' | 'sheet' | 'both' | 'hidden')

Granting Permissions

When you first open the Bookmarks feature:
1

Click Bookmarks Icon

Click the Bookmarks icon in the dock or top bar.
2

Permission Prompt

You’ll see a screen saying “Bookmarks permission required” with a Grant Permission button.
3

Browser Prompt

Your browser will show a popup requesting permission to:
  • Read and change your bookmarks
4

Click 'Allow'

Grant the permission to enable bookmark access.
5

Automatic Sync

The extension immediately syncs your bookmarks and displays them.
From bookmarks.sheet.tsx:134-160:
if (!hasPermissions) {
  return (
    <div className="flex flex-1 flex-col items-center justify-center gap-4">
      <Bookmark className="h-12 w-12 text-white/40" />
      <div className="text-lg text-white">
        {t("bookmarks.needs-permission")}
      </div>
      <Button onClick={handleRequestPermissions} variant="outline">
        {t("bookmarks.grant-permission")}
      </Button>
    </div>
  );
}
From bookmarks.store.ts:370-413, the permission request:
requestPermissions: async () => {
  const granted = await chrome.permissions.request({
    permissions: ["bookmarks"],
  });
  set({ hasPermissions: granted });
  if (granted) {
    await get().syncFromChrome();
  }
  return granted;
}
You can revoke the bookmarks permission at any time from your browser’s extension settings. Your cached bookmarks will remain visible until you clear extension data.

Viewing Bookmarks

Bookmarks Tree Structure

From bookmarks.store.ts:49-58, Chrome bookmarks are mapped to:
interface BookmarkNode {
  id: string;
  title: string;
  url?: string;              // Folders don't have URLs
  parentId?: string;
  index?: number;
  dateAdded?: number;
  dateGroupModified?: number;
  children?: BookmarkNode[]; // Nested structure
}
From bookmarks.store.ts:111-143, bookmarks are separated into:
interface BookmarkFolder {
  id: string;
  title: string;
  parentId?: string;
  children: BookmarkNode[];
}

interface BookmarkLink {
  id: string;
  title: string;
  url: string;
  favicon?: string;
  parentId?: string;
  dateAdded: number;
}
The tree view shows:
  • Folders with folder icon and expand/collapse chevron
  • Links with favicon (or bookmark icon fallback) and external link icon
From bookmarks.sheet.tsx:207-304:
<BookmarkTreeNode>
  {isFolder ? (
    <Folder icon with expandable children>
  ) : (
    <Link with favicon and URL>
  )}
</BookmarkTreeNode>

Opening Bookmarks

Click any bookmark link to open it: From bookmarks.sheet.tsx:110-116:
const handleOpenLink = (url: string) => {
  if (chrome?.tabs) {
    chrome.tabs.create({ url });
  } else {
    window.open(url, "_blank", "noopener,noreferrer");
  }
};
  • In extension: Opens in new browser tab
  • Maintains your current Meelio new tab
  • No navigation away from your dashboard

Bookmark Organization

Folder Navigation

1

Find Folder

Folders appear with a folder icon and show the number of items inside (e.g., “Work (23)”).
2

Expand Folder

Click the folder to expand it. A chevron-down icon indicates the folder is expanded.
3

View Contents

All bookmarks and subfolders appear indented under the parent folder.
4

Collapse Folder

Click the folder again to collapse it and hide its contents.
From bookmarks.sheet.tsx:224-268:
const isExpanded = expandedFolders.has(node.id);

<button onClick={() => onToggleFolder(node.id)}>
  {isExpanded ? <ChevronDown /> : <ChevronRight />}
  <Folder />
  <span>{node.title}</span>
  {hasChildren && <span>({node.children?.length})</span>}
</button>

{hasChildren && isExpanded && (
  <div className="ml-4 mt-1 space-y-1">
    {node.children?.map((child) => (
      <BookmarkTreeNode {...child} />
    ))}
  </div>
)}

Favicon Display

From bookmarks.sheet.tsx:272-294, bookmarks show favicons:
const faviconUrl = node.url ? getFaviconUrl(node.url) : null;
const [showFallback, setShowFallback] = useState(false);

{faviconUrl && !showFallback ? (
  <img
    src={faviconUrl}
    onError={() => setShowFallback(true)}
  />
) : (
  <Bookmark className="h-4 w-4 text-white/40" />
)}
If a favicon fails to load, it gracefully falls back to a bookmark icon.

Syncing Bookmarks

Automatic Sync

From bookmarks.store.ts:282-288:
initializeStore: async () => {
  const hasPerms = await get().checkPermissions();
  await get().loadFromLocal();
  
  const needsSync = hasPerms && (shouldRefresh(get().lastSyncAt) || get().links.length === 0);
  if (needsSync) {
    await get().syncFromChrome();
  }
}
Bookmarks sync automatically:
  • On extension load (if cache is older than 1 hour)
  • When you first grant permissions
  • When cache is empty
  • When you manually click refresh

Manual Refresh

1

Open Bookmarks Panel

Click the Bookmarks icon to open the full sheet.
2

Click Refresh Button

At the top of the panel, click the Refresh button (rotating arrow icon).
3

Syncing

The icon spins while syncing. Wait for it to complete.
4

Updated

Your bookmarks are now up-to-date with your browser’s bookmark manager.
From bookmarks.sheet.tsx:174-183:
<Button
  variant="outline"
  onClick={refreshBookmarks}
  disabled={isLoading}
>
  <RefreshCw className={cn("h-4 w-4", isLoading && "animate-spin")} />
  {t("bookmarks.refresh")}
</Button>

Live Updates

From bookmarks.store.ts:545-561, the extension listens for bookmark changes:
function setupBookmarkListeners(): void {
  if (!hasBookmarksApi()) return;

  const syncIfAuthorized = (): void => {
    const store = useBookmarksStore.getState();
    if (store.hasPermissions) {
      store.syncFromChrome();
    }
  };

  chrome.bookmarks.onCreated.addListener(syncIfAuthorized);
  chrome.bookmarks.onChanged.addListener(syncIfAuthorized);
  chrome.bookmarks.onRemoved.addListener(syncIfAuthorized);
  chrome.bookmarks.onMoved.addListener(syncIfAuthorized);
}
When you:
  • Create a bookmark in your browser
  • Edit a bookmark title or URL
  • Delete a bookmark
  • Move a bookmark to a different folder
Meelio automatically re-syncs and updates the display.

Bookmark Caching

Cache Storage

From bookmarks.store.ts:145-170, bookmarks are cached in IndexedDB:
interface CachedBookmark {
  id: string;               // Meelio ID
  userId: string;           // User ID
  chromeId: string;         // Browser bookmark ID
  title: string;
  url?: string;
  parentId?: string;
  index?: number;
  dateAdded: number;
  dateGroupModified?: number;
  cachedAt: number;         // Cache timestamp
  deletedAt: null;
}

Cache Benefits

  • Fast Loading: No delay waiting for Chrome API on every page load
  • Offline Access: View cached bookmarks even if sync temporarily fails
  • Performance: Tree building from cache is faster than Chrome API
  • Resilience: Fallback if permission is temporarily revoked
From bookmarks.store.ts:220-228:
const loadFromCache = async (userId: string): Promise<BookmarkNode[]> => {
  const cached = await db.bookmarks
    .where("userId")
    .equals(userId)
    .filter((b) => !b.deletedAt)
    .sortBy("index");

  return buildTreeFromCache(cached);
};

Cache Refresh Logic

From bookmarks.store.ts:230-233:
const shouldRefresh = (lastSyncAt: number | null): boolean => {
  if (!lastSyncAt) return true;
  return Date.now() - lastSyncAt > CACHE_DURATION; // 1 hour
};
Cache is automatically refreshed after 1 hour to ensure you see recent bookmark changes.

Managing Bookmarks

Add Bookmark

From bookmarks.store.ts:424-438:
addBookmark: async (bookmark) => {
  if (!hasBookmarksApi()) return;

  await chrome.bookmarks.create({
    title: bookmark.title,
    url: bookmark.url,
    parentId: bookmark.parentId,
  });
  await get().syncFromChrome();
}
Currently, adding bookmarks through Meelio UI is not exposed. Use your browser’s native bookmark manager or bookmark bar to add bookmarks. Meelio will auto-sync them.

Edit Bookmark

From bookmarks.store.ts:440-453:
updateBookmark: async (id, updates) => {
  const changes: chrome.bookmarks.BookmarkChangesArg = {};
  if (updates.title !== undefined) changes.title = updates.title;
  if (updates.url !== undefined) changes.url = updates.url;

  await chrome.bookmarks.update(id, changes);
  await get().syncFromChrome();
}
To edit bookmarks:
  1. Use your browser’s native bookmark manager
  2. Edit the bookmark there
  3. Meelio automatically syncs the changes

Delete Bookmark

From bookmarks.store.ts:455-466:
deleteBookmark: async (id) => {
  await chrome.bookmarks.remove(id);
  await get().syncFromChrome();
}
To delete bookmarks:
  1. Use your browser’s native bookmark manager
  2. Delete the bookmark
  3. Meelio removes it from the display on next sync

Folder Management

From bookmarks.store.ts:468-508, folders can be:
// Add folder
addFolder: async (folder) => {
  await chrome.bookmarks.create({
    title: folder.title,
    parentId: folder.parentId,
  });
}

// Update folder
updateFolder: async (id, updates) => {
  await chrome.bookmarks.update(id, { title: updates.title });
}

// Delete folder (and all contents)
deleteFolder: async (id) => {
  await chrome.bookmarks.removeTree(id);
}
Deleting a folder removes all bookmarks and subfolders inside it. Use your browser’s bookmark manager for folder operations.

Statistics Display

From bookmarks.sheet.tsx:184-189:
<div className="text-sm text-white/60">
  {links.length} {t("bookmarks.links")}
  {folders.length > 0 &&
    ` • ${folders.length} ${t("bookmarks.folders")}`}
</div>
The bookmarks panel header shows:
  • Total number of bookmark links
  • Total number of folders
  • Updates in real-time as you sync

Empty State

From bookmarks.sheet.tsx:192-199:
{bookmarks.length === 0 ? (
  <div className="flex flex-col items-center justify-center gap-4">
    <Bookmark className="h-12 w-12 text-white/20" />
    <div>{t("bookmarks.empty")}</div>
  </div>
) : (
  <BookmarkTree nodes={bookmarks} onLinkClick={handleOpenLink} />
)}
If you have no bookmarks in your browser, Meelio shows an empty state with a bookmark icon and “No bookmarks found” message.

Extension-Only Feature

From bookmarks.sheet.tsx:345-370:
const isExtension = typeof chrome !== "undefined" && !!chrome.storage;
Bookmarks require browser extension APIs for:
  • Accessing the Chrome bookmarks API
  • Reading the bookmark tree
  • Listening to bookmark changes
The web version shows:
"Bookmarks available in extension only"
Bookmarks only work in the browser extension. The feature cannot function in the web app version since it requires native browser bookmark access.

Browser Compatibility

Full Support
  • Native chrome.bookmarks API
  • All bookmark operations supported
  • Live sync with bookmark changes
  • Folder hierarchy fully supported

Privacy & Data

  • Bookmarks are read from your browser’s native bookmark storage
  • A cached copy is stored locally in IndexedDB for performance
  • No bookmark data is sent to Meelio servers
  • Bookmark URLs and titles remain private to your browser profile
  • Optional: Sync bookmark cache across devices when signed in to Meelio
Meelio never uploads your bookmark URLs to external servers. The cache is local-only for performance.

Troubleshooting

  • Check if you’ve already granted/denied the permission in the past
  • Go to browser settings > Extensions > Meelio > Permissions
  • Manually enable “Read and change your bookmarks”
  • Refresh the Meelio tab after granting permission
  • Verify you have bookmarks in your browser’s bookmark manager
  • Click the Refresh button to manually sync
  • Check if you’re signed in to the correct browser profile
  • Check browser console for sync errors
  • Some sites don’t have favicons
  • Favicon URLs may be blocked by content security policy
  • Extension falls back to bookmark icon automatically
  • This is expected behavior for certain sites
  • Wait for auto-sync (happens within 1 hour)
  • Click the Refresh button to force immediate sync
  • Check if bookmark change listeners are working (console logs)
  • Verify you have the latest extension version
  • Ensure you’re clicking the folder row (not just the icon)
  • Check if JavaScript is enabled
  • Try refreshing the page
  • Check browser console for React errors

Tab Stash

Save temporary tab sessions vs. permanent bookmarks

Search

Search your bookmarks from the universal search bar

Dock Customization

Configure the bookmarks icon position in the dock

Settings

Change bookmark display mode (bar, sheet, both, hidden)

Build docs developers (and LLMs) love