Capturing URLs to Obsidian with a Bookmarklet
On This Page:
Many of us come across interesting articles, tools, or resources online and wish to save them for later reference. Over the last couple of years I've tried using an application to keep all of my 'notes' in one place and recently moved to Obsidian.
I wanted a bookmarklet that would help me save interesting web pages to my Obsidian Vault so I can access it at a later date. I could use the browsers native bookmarking tools but I'm often across browsers throughout my working day. I could use available third-party tools - but I want to try to keep 'everything' in markdown, in Obsidian. This article you're reading now started off in Obsidian.
In this post, I’ll walk you through the bookmarklet, how it works, and how you can use it to streamline your bookmarking workflow.
What the Bookmarklet Does
This bookmarklet allows you to:
- Capture the current webpage title and URL.
- Optionally modify the title.
- Add tags for easier categorisation.
- Extract metadata like descriptions, images, and publication dates when available.
- Save all this information directly into your specified folder in your Obsidian vault.
The full code
javascript:(function () {const vault = 'everything'; // Your Obsidian vault nameconst folder = 'bookmarks'; // Folder for all bookmarkslet title = prompt('Want to change the title?', document.title);const askForTags = prompt('Enter any tags, comma separated');let tags = askForTags ? askForTags.split(',') : [];// Function to format tags: remove spaces, replace with hyphens, add #function formatHashtags(arr) {return arr.map((str) => '#' + str.trim().replace(/\s+/g, '-')).join(' ');}tags = formatHashtags(tags);const url = document.location.href;// Try to grab meta description or OG descriptionlet description = '';const metaDescription = document.querySelector('meta[name="description"]');const ogDescription = document.querySelector('meta[property="og:description"]');if (metaDescription) {description = metaDescription.content;} else if (ogDescription) {description = ogDescription.content;}// Try to grab OG image, Twitter image, or other social imagelet imageUrl = '';const ogImage = document.querySelector('meta[property="og:image"]');const twitterImage = document.querySelector('meta[name="twitter:image"]');if (ogImage) {imageUrl = ogImage.content;} else if (twitterImage) {imageUrl = twitterImage.content;}// Try to grab article:published_timelet publishedDate = '';const publishedTime = document.querySelector('meta[property="article:published_time"]');if (publishedTime) {const pubDate = new Date(publishedTime.content);const pubDay = String(pubDate.getDate()).padStart(2, '0');const pubMonth = String(pubDate.getMonth() + 1).padStart(2, '0'); // Months are zero-indexedconst pubYear = pubDate.getFullYear();publishedDate = `Published on ${pubDay}-${pubMonth}-${pubYear}\n\n`; // Format as DD-MM-YYYY}// Sanitise title to remove invalid filename characterstitle = title.replace(/[\\/:*?"<>|]/g, '-');// Generate DD-MM-YYYY timestamp formatconst now = new Date();const day = String(now.getDate()).padStart(2, '0');const month = String(now.getMonth() + 1).padStart(2, '0'); // Months are zero-indexedconst year = now.getFullYear();const formattedDate = `${day}-${month}-${year}`; // DD-MM-YYYY formatconst fileName = `${folder}/${title}`; // Unique file name// Format content with title, date, image, description, URL, and tagsconst imageMarkdown = imageUrl ? `\n\n` : ''; // Add markdown image if URL existsconst content = `${publishedDate}Saved on ${formattedDate}\n\n${imageMarkdown}${description ? description + '\n\n' : ''}- [Visit the page](${url})\n\n${tags}\n`;document.location.href = `obsidian://new?file=${encodeURIComponent(fileName)}&content=${encodeURIComponent(content)}&vault=${vault}`;})();
Breaking Down the Code
The bookmarklet uses JavaScript to gather data from the current webpage and construct a new note in your Obsidian vault. Let’s break it into steps.
Setting Up the Vault and Folder
const vault = 'everything'; // Your Obsidian vault nameconst folder = 'bookmarks'; // Folder for bookmarks in your vault
These constants define where the captured notes will be stored. Replace everything
and bookmarks
with the name of your vault and the desired folder path.
Prompting for the Title and Tags
let title = prompt('Want to change the title?', document.title);const askForTags = prompt('Enter any tags, comma separated');let tags = askForTags ? askForTags.split(',') : [];
Title: The script captures the webpage’s title (document.title) and gives you the option to modify it via a prompt. Tags: You can enter tags separated by commas to categorise the bookmark. If you skip this, the tags will remain empty.
Formatting Tags
function formatHashtags(arr) {return arr.map((str) => '#' + str.trim().replace(/\s+/g, '-')).join(' ');}tags = formatHashtags(tags);
This function:
- Trims whitespace from each tag.
- Replaces spaces with hyphens to make tags URL-friendly.
- Prepends a # to each tag, turning them into Obsidian-compatible hashtags.
Extracting Metadata
Description
const metaDescription = document.querySelector('meta[name="description"]');const ogDescription = document.querySelector('meta[property="og:description"]');let description = metaDescription ? metaDescription.content : ogDescription ? ogDescription.content : '';
This retrieves the webpage’s description from tags.
Image
const ogImage = document.querySelector('meta[property="og:image"]');const twitterImage = document.querySelector('meta[name="twitter:image"]');let imageUrl = ogImage ? ogImage.content : twitterImage ? twitterImage.content : '';
The script looks for Open Graph or Twitter image metadata to include a thumbnail.
Published Date
const publishedTime = document.querySelector('meta[property="article:published_time"]');let publishedDate = '';if (publishedTime) {const pubDate = new Date(publishedTime.content);publishedDate = `Published on ${pubDate.toLocaleDateString()}\n\n`;}
It checks for the publication date in the tags and formats it.
Sanitising the Title
title = title.replace(/[\\/:*?"<>|]/g, '-');
This step ensures the title is valid for file names by removing characters that might cause issues.
Constructing the Note
const imageMarkdown = imageUrl ? `\n\n` : '';const content = `${publishedDate}Saved on ${formattedDate}\n\n${imageMarkdown}${description ? description + '\n\n' : ''}- [Visit the page](${url})\n\n${tags}\n`;
The script then formats the note with the collected data:
- Includes metadata (date, description, and image).
- Appends the URL.
- Adds any tags you’ve entered.
Sending the Note to Obsidian
document.location.href = `obsidian://new?file=${encodeURIComponent(fileName)}&content=${encodeURIComponent(content)}&vault=${vault}`;
Finally, the script redirects you to Obsidian’s URI scheme to create the note. This sends the note to the specified folder in your vault using Obsidian’s obsidian://
protocol.
How to Use the Bookmarklet
- Customise It:
- Copy the full JavaScript code.
- Edit the vault and folder variables to match your Obsidian setup.
- Create the Bookmarklet:
- Create a new bookmark in Safari and paste the code into the URL field.
- Use It:
- While browsing, click the bookmarklet.
- Enter a title and tags (optional).
- The note will appear in your Obsidian vault.
These steps should work in all modern browsers, I've only used this in Safari.
Hot Linking
When you save a url as a bookmark in Obsidian the image that is save d is the url, we are hot-linking to someone else's resource on their server. That's not nice, so I created a node script that would
- look into the bookmarks folder
- find the image url in a markdown file
- download the image to an
assets
folder in my vault - replace the hot link with the local link
Here's the script:
const fs = require('fs');const axios = require('axios');const path = require('path');const obsidianVaultPath = '/your/path/to/your/obsidian/vault';const bookmarksFolderPath = path.join(obsidianVaultPath, 'your/bookmark/folder');const imageSavePath = path.join(bookmarksFolderPath, 'your/images/folder');if (!fs.existsSync(imageSavePath)) {fs.mkdirSync(imageSavePath, { recursive: true });}// Download the image and save it locallyasync function downloadImage(imageUrl, saveFolder) {try {const response = await axios.get(imageUrl, { responseType: 'arraybuffer' });if (response.status === 200) {const imageFilename = path.join(saveFolder, path.basename(imageUrl));fs.writeFileSync(imageFilename, response.data);console.log(`Image downloaded and saved as: ${imageFilename}`);return imageFilename;}} catch (error) {console.error('Error downloading image:', error);}}// Process a noteasync function processNote(noteFilePath) {try {let noteContent = fs.readFileSync(noteFilePath, 'utf-8');const imageRegex = /!\[\]\((http[s]?:\/\/[^\)]+)\)/g;const matches = [...noteContent.matchAll(imageRegex)];if (matches.length > 0) {for (const match of matches) {const imageUrl = match[1];console.log(`Found image URL: ${imageUrl}`);const localImagePath = await downloadImage(imageUrl, imageSavePath);if (localImagePath) {const localImageFilename = path.basename(localImagePath);noteContent = noteContent.replace(imageUrl, `../assets/${localImageFilename}`);}}fs.writeFileSync(noteFilePath, noteContent, 'utf-8');console.log(`Note updated: ${noteFilePath}`);} else {console.log(`No image URLs found in: ${noteFilePath}`);}} catch (error) {console.error(`Error processing note: ${noteFilePath}`, error);}}// Process all markdown files in the bookmarks folderfunction processAllNotes() {try {const files = fs.readdirSync(bookmarksFolderPath);const mdFiles = files.filter(file => file.endsWith('.md'));mdFiles.forEach(file => {const noteFilePath = path.join(bookmarksFolderPath, file);processNote(noteFilePath);});} catch (error) {console.error('Error processing files:', error);}}processAllNotes();
You will need to update the obsidianVaultPath
and bookmarksFolderPath
and also install axios (npm install axios
).
There's a few issues with this script that I want to address -- sometimes the OG image is not a png, it seems Obisdian doesn't support something like WebP. If the image returned isn't recognised you would get the text of the file (better than hot-linking in my opinion).
In the future, I plan to share this on GitHub to make it easier for others to explore and contribute. I might even refine it further, like converting all images to PNG for consistency.
Initially, I considered adding a ‘to be sorted’ folder to organise bookmarks, but I realised tags provide an equally efficient solution.
I hope this inspires you to craft a bookmarking system that fits your workflow, especially if you’re using Obsidian.