## File: README.md
Obsidian Web Clipper helps you highlight and capture the web in your favorite browser. Anything you save is stored as durable Markdown files that you can read offline, and preserve for the long term.
- **[Download Web Clipper](https://obsidian.md/clipper)**
- **[Documentation](https://help.obsidian.md/web-clipper)**
- **[Troubleshooting](https://help.obsidian.md/web-clipper/troubleshoot)**
## Get started
Install the extension by downloading it from the official directory for your browser:
- **[Chrome Web Store](https://chromewebstore.google.com/detail/obsidian-web-clipper/cnjifjpddelmedmihgijeibhnjfabmlf)** for Chrome, Brave, Arc, Orion, and other Chromium-based browsers.
- **[Firefox Add-Ons](https://addons.mozilla.org/en-US/firefox/addon/web-clipper-obsidian/)** for Firefox and Firefox Mobile.
- **[Safari Extensions](https://apps.apple.com/us/app/obsidian-web-clipper/id6720708363)** for macOS, iOS, and iPadOS.
- **[Edge Add-Ons](https://microsoftedge.microsoft.com/addons/detail/obsidian-web-clipper/eigdjhmgnaaeaonimdklocfekkaanfme)** for Microsoft Edge.
## Use the extension
Documentation is available on the [Obsidian Help site](https://help.obsidian.md/web-clipper), which covers how to use [highlighting](https://help.obsidian.md/web-clipper/highlight), [templates](https://help.obsidian.md/web-clipper/templates), [variables](https://help.obsidian.md/web-clipper/variables), [filters](https://help.obsidian.md/web-clipper/filters), and more.
## Contribute
### Translations
You can help translate Web Clipper into your language. Submit your translation via pull request using the format found in the [/_locales](/src/_locales) folder.
### Features and bug fixes
See the [help wanted](https://github.com/obsidianmd/obsidian-clipper/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) tag for issues where contributions are welcome.
## Roadmap
In no particular order:
- [ ] Annotate highlights
- [ ] Template directory
- [ ] Sync settings across browsers
- [x] A separate icon for Web Clipper (1.6.3)
- [x] Template validation (1.1.0)
- [x] Template logic (if/for) (1.1.0)
- [x] Save images locally ([Obsidian 1.8.0](https://obsidian.md/changelog/2024-12-18-desktop-v1.8.0/))
- [x] Translate UI into more languages — help is welcomed
## Developers
To build the extension:
```
npm run build
```
This will create three directories:
- `dist/` for the Chromium version
- `dist_firefox/` for the Firefox version
- `dist_safari/` for the Safari version
### Install the extension locally
For Chromium browsers, such as Chrome, Brave, Edge, and Arc:
1. Open your browser and navigate to `chrome://extensions`
2. Enable **Developer mode**
3. Click **Load unpacked** and select the `dist` directory
For Firefox:
1. Open Firefox and navigate to `about:debugging#/runtime/this-firefox`
2. Click **Load Temporary Add-on**
3. Navigate to the `dist_firefox` directory and select the `manifest.json` file
If you want to run the extension permanently you can do so with the Nightly or Developer versions of Firefox.
1. Type `about:config` in the URL bar
2. In the Search box type `xpinstall.signatures.required`
3. Double-click the preference, or right-click and select "Toggle", to set it to `false`.
4. Go to `about:addons` > gear icon > **Install Add-on From File…**
For iOS Simulator testing on macOS:
1. Run `npm run build` to build the extension
2. Open `xcode/Obsidian Web Clipper/Obsidian Web Clipper.xcodeproj` in Xcode
3. Select the **Obsidian Web Clipper (iOS)** scheme from the scheme selector
4. Choose an iOS Simulator device and click **Run** to build and launch the app
5. Once the app is running on the simulator, open **Safari**
6. Navigate to a webpage and tap the **Extensions** button in Safari to access the Web Clipper extension
### Run tests
```
npm test
```
Or run in watch mode during development:
```
npm run test:watch
```
## Third-party libraries
- [webextension-polyfill](https://github.com/mozilla/webextension-polyfill) for browser compatibility
- [defuddle](https://github.com/kepano/defuddle) for content extraction and Markdown conversion
- [dayjs](https://github.com/iamkun/dayjs) for date parsing and formatting
- [lz-string](https://github.com/pieroxy/lz-string) to compress templates to reduce storage space
- [lucide](https://github.com/lucide-icons/lucide) for icons
- [dompurify](https://github.com/cure53/DOMPurify) for sanitizing HTML
## License
Obsidian Web Clipper source code is open source under the MIT License. All trademarks, icons, marketing copy, and other marketing assets are excluded from that license.
---
## File: docs/Clip web pages.md
---
permalink: web-clipper/capture
aliases:
- Obsidian Web Clipper/Capture web pages
---
Once you install the [[Introduction to Obsidian Web Clipper|Web Clipper]] browser extension, you can access it in several ways, depending on your browser:
1. The Obsidian icon in your browser toolbar.
2. Hotkeys, to activate the extension from your keyboard.
3. Context menu, by right-clicking the web page you are visiting.
To save a page to Obsidian click the **Add to Obsidian** button.
## Capture a page
When you open the extension, Web Clipper extracts data from the current web page following the settings in your [[Obsidian Web Clipper/Templates|template]]. You can create your own templates, and customize the output using [[variables]] and [[filters]].
By default Web Clipper attempts to intelligently extract only the main article content, excluding other elements on the page. However, you can override this behavior in the following ways:
- If a custom template is present it uses your template.
- If a selection is present, it uses the selection. You can use `Ctrl/Cmd+A` to select the entire page.
- If any [[Highlight web pages|highlights]] are present, it uses the highlights.
## Download images
Images are not automatically downloaded when you use Web Clipper. Instead, images link to their web-based URL. This saves space in your vault but it means the images will not be accessible offline, or if the URL stops working.
You can download images for any file in Obsidian using the [[Command palette|command]] named **Download attachments for current file**. This command can also be mapped to a hotkey in Obsidian.
## Hotkeys
Web Clipper includes keyboard shortcuts you can use to speed up your workflow. To change key mappings go to **Web Clipper Settings** → **General** and follow the instructions for your browser. Mappings can be changed for all browsers except Safari which does not support editing hotkeys.
| Action | macOS | Windows/Linux |
| ----------------------- | ------------- | -------------- |
| Open clipper | `Cmd+Shift+O` | `Ctrl+Shift+O` |
| Quick clip | `Opt+Shift+O` | `Alt+Shift+O` |
| Toggle highlighter mode | `Opt+Shift+H` | `Alt+Shift+H` |
## Interface functionality
The Web Clipper interface is divided into four sections:
1. **Header** where you can switch templates, turn on [[Highlight web pages|highlighting]], and access settings.
2. **Properties** shows the [[Properties|metadata]] extracted from the page that will be saved as [[Properties]] in Obsidian.
3. **Note content** that will be saved to Obsidian.
4. **Footer** allows you select the vault and folder, and add to Obsidian.
Header functionality includes:
- **Template** dropdown to switch between your saved [[Obsidian Web Clipper/Templates|templates]] added in Web Clipper settings.
- **More (...)** button to display page variables you can use in templates.
- **Highlighter** button to turn on [[Highlight web pages|highlighting]].
- **Cog** button to open Web Clipper settings.
Footer functionality includes:
- **Add to Obsidian** button to save data to Obsidian.
- **Vault** dropdown to switch between saved vaults added in Web Clipper settings.
- **Folder** field to define which folder to save to.
- **Interpreter** to run [[Interpret web pages|natural language prompts]] on the page.
---
## File: docs/Filters.md
---
permalink: web-clipper/filters
---
Filters allow you to modify [[variables]] in [[Obsidian Web Clipper/Templates|Web Clipper templates]]. Filters are applied to variables using the syntax `{{variable|filter}}`.
- Filters work for any kind of [[Variables|variable]] including `prompt`, `meta`, `selector`, and `schema` variables.
- Filters can be chained, e.g. `{{variable|filter1|filter2}}`, and are applied in the order they are added.
## Dates
Convert and modify dates.
### `date`
Converts a date to the specified format, [see reference](https://day.js.org/docs/en/display/format).
- `{{date|date:"YYYY-MM-DD"}}` converts the current date to "YYYY-MM-DD".
- Use `date:("outputFormat", "inputFormat")` to specify the input format, e.g. `"12/01/2024"|date:("YYYY-MM-DD", "MM/DD/YYYY")` parses "12/01/2024" and returns `"2024-12-01"`.
### `date_modify`
Modifies a date by adding or subtracting a specified amount of time, [see reference](https://day.js.org/docs/en/manipulate/add).
- `"2024-12-01"|date_modify:"+1 year"` returns `"2025-12-01"`
- `"2024-12-01"|date_modify:"- 2 months"` returns `"2024-10-01"`
### `duration`
Converts ISO 8601 duration strings or seconds into formatted time strings. Uses tokens: `HH` (padded hours), `H` (hours), `mm` (padded minutes), `m` (minutes), `ss` (padded seconds), `s` (seconds).
- `"PT1H30M"|duration:"HH:mm:ss"` returns `"01:30:00"`.
- `"3665"|duration:"H:mm:ss"` returns `"1:01:05"`.
- Setting `duration` without any parameters uses `HH:mm:ss` over 1 hour, `mm:ss` under 1 hour.
- Supports both ISO 8601 duration strings (e.g., `PT6702S`, `PT1H30M`) and plain seconds.
## Text conversion and capitalization
Convert text strings from one format to another.
### `camel`
Converts text to `camelCase`.
### `capitalize`
Capitalizes the first character of the value and converts the rest to lowercase, e.g. `"hELLO wORLD"|capitalize` returns `"Hello world"`.
### `decode_uri`
Decodes a URI-encoded string, e.g. `"%E4%BD%A0%E5%A5%BD"|decode_uri` returns `"你好"`.
- `"hello%20world"|decode_uri` returns `"hello world"`.
- Returns the original string if decoding fails (e.g. malformed URI sequences).
### `kebab`
Converts text to `kebab-case`.
### `lower`
Converts text to `lowercase`.
### `pascal`
Converts text to `PascalCase`.
### `replace`
Replaces occurrences of specified text:
- Simple replacement: `"hello!"|replace:",":""` removes all commas.
- Multiple replacements: `"hello world"|replace:("e":"a","o":"0")` returns `"hall0 w0rld"`.
- Replacements are applied in the order they are specified.
- To remove specified text, use `""` as the replacement value.
- Special characters including `: | { } ( ) ' "` should be escaped with a backslash when used in the search term, e.g. `\:` to search for a literal colon.
Regex is supported using JavaScript regex syntax:
- Replace all vowels: `"hello world"|replace:"/[aeiou]/g":"*"` → `"h*ll* w*rld".`
- Case-insensitive: `"HELLO world"|replace:"/hello/i":"hi"` → `"hi world".`
- Multiple regex: `"hello world"|replace:("/[aeiou]/g":"*","/\s+/":"-")` → `"h*ll*-w*rld"`.
- Available flags: `g` (global), `i` (case-insensitive), `m` (multiline), `s` (dotAll), `u` (unicode), `y` (sticky).
### `safe_name`
Converts text to a safe file name.
- By default, `safe_name` applies the most conservative sanitization rules.
- OS-specific rules can be applied with `safe_name:os` where `os` can be `windows`, `mac`, or `linux` to only apply the rules for that operating system.
### `snake`
Converts text to `snake_case`.
### `title`
Converts text to `Title Case`, e.g. `"hello world"|title` returns `"Hello World"`.
### `trim`
Removes white space from both ends of a string.
- `" hello world "|trim` returns `"hello world"`.
### `uncamel`
Converts camelCase or PascalCase to space-separated words, which you can further format with other filters like `title` or `capitalize`.
- `"camelCase"|uncamel` returns `"camel case"`.
- `"PascalCase"|uncamel` returns `"pascal case"`.
### `upper`
Converts a value to uppercase, e.g. `"hello world"|upper` returns `"HELLO WORLD"`.
## Text formatting
Apply [[Basic formatting syntax]] and [[Advanced formatting syntax]] to text.
### `blockquote`
Adds a Markdown quote prefix (`> `) to each line of the input.
### `callout`
Creates a [[Callouts|callout]] with optional parameters: `{{variable|callout:("type", "title", foldState)}}`
- `type` is the callout type, and defaults to "info"
- `title` is the callout title, and defaults to empty
- `foldState` is a boolean to set the fold state (true for folded, false for unfolded, null for not foldable)
### `footnote`
Converts an array or object into a list of Markdown footnotes.
- For arrays: `["first item","second item"]|footnote` returns: `[^1]: first item` etc.
- For objects: `{"First Note": "Content 1", "Second Note": "Content 2"}|footnote` returns: `[^first-note]: Content 1` etc.
### `fragment_link`
Converts strings and arrays into [text fragment](https://developer.mozilla.org/en-US/docs/Web/URI/Fragment/Text_fragments) links. Defaults to "link" for the link text.
- `highlights|fragment_link` returns `Highlight content [link](text-fragment-url)`
- `highlights|fragment_link:"custom title"` returns `Highlight content [custom title](text-fragment-url)`
### `image`
Converts strings, arrays, or objects into Markdown image syntax.
- For strings: `"image.jpg"|image:"alt text"` returns ``.
- For arrays: `["image1.jpg","image2.jpg"]|image:"alt text"` returns an array of Markdown image strings with the same alt text for all images.
- For objects: `{"image1.jpg": "Alt 1", "image2.jpg": "Alt 2"}|image` returns Markdown image strings with alt text from the object keys.
### `link`
Converts strings, arrays, or objects into Markdown link syntax (not to be confused with [[Filters#`wikilink`|wikilink]]).
- For strings: `"url"|link:"author"` returns `[author](url)`.
- For arrays: `["url1","url2"]|link:"author"` returns an array of Markdown links with the same text for all links.
- For objects: `{"url1": "Author 1", "url2": "Author 2"}|link` returns Markdown links with the text that matches the object keys.
### `list`
Converts an array to a Markdown list.
- `list` to convert to a bullet list.
- `list:task` to convert to a task list.
- `list:numbered` to convert to a numbered list.
- `list:numbered-task` to convert to a task list with numbers.
### `table`
Converts an array or array of objects into a [[Advanced formatting syntax#Tables|Markdown table]]:
- For an array of objects, it uses the object keys as headers.
- For an array of arrays, it creates a table with each nested array as a row.
- For a simple array, it creates a single-column table with "Value" as the header.
- Custom column headers can be specified using: `table:("Column 1", "Column 2", "Column 3")`. When used with a simple array, it automatically breaks the data into rows based on the number of columns specified.
### `wikilink`
Converts strings, arrays, or objects into Obsidian [[Link notes|wikilink]] syntax.
- For strings: `"page"|wikilink` returns `[[page]]`.
- For strings with alias: `"page"|wikilink:"alias"` returns `[[page|alias]]`.
- For arrays: `["page1","page2"]|wikilink` returns an array of wikilinks without aliases.
- For arrays with alias: `["page1","page2"]|wikilink:"alias"` returns an array of wikilinks with the same alias for all links.
- For objects: `{"page1": "alias1", "page2": "alias2"}|wikilink` returns wikilinks with the keys as page names and values as aliases.
## Numbers
### `calc`
Performs basic arithmetic operations on numbers.
- Supports operators: `+`, `-`, `*`, `/`, `**` (or `^`) for exponentiation.
- Example: `5|calc:"+10"` returns `15`.
- Example: `2|calc:"**3"` returns `8` (2 cubed).
- Returns the original string if the input is not a number.
### `length`
Returns the length of strings, arrays, or number of keys in objects.
- For strings: `"hello"|length` returns `5`.
- For arrays: `["a","b","c"]|length` returns `3`.
- For objects: `{"a":1,"b":2}|length` returns `2`.
### `round`
Rounds a number to the nearest integer or to a specified number of decimal places.
- Without parameters: `3.7|round` returns `4`.
- With decimal places specified: `3.14159|round:2` returns `3.14`.
## HTML processing
Process HTML content and convert HTML to Markdown. Note that your input [[Variables|variable]] must contain HTML content, e.g. using `{{fullHtml}}`, `{{contentHtml}}` or a `{{selectorHtml:}}` variable.
### `markdown`
Converts a string to an [[Obsidian Flavored Markdown]] formatted string.
- Useful when combined with variables that return HTML such as `{{contentHtml}}`, `{{fullHtml}}`, and selector variables like `{{selectorHtml:cssSelector}}`.
### `remove_attr`
Removes only the specified HTML attributes from tags.
- Example: `"Content"|remove_attr:"class"` returns `Content`.
- Multiple attributes: `{{fullHtml|remove_attr:("class,style,id")}}`
### `remove_html`
Removes the specified HTML elements and their content from a string.
- Supports tag name, class, or id, e.g. `{{fullHtml|remove_html:("img,.class-name,#element-id")}}`
- To remove only HTML tags or attributes without removing the content use the `strip_tags` or `strip_attr` filters.
### `remove_tags`
Removes only the specified HTML tags. Keeps the content of the tags.
- Example: `"Hello **world**!"|remove_tags:"b"` returns `"Hello world!"`.
- Multiple tags: `{{fullHtml|remove_tags:("a,em,strong")}}`
### `replace_tags`
Replaces HTML tags, maintaining the content and attributes of the tag.
- `{{fullHtml|replace_tags:"strong":"h2"}}` replaces all `` tags with `
`.
### `strip_attr`
Removes **all** HTML attributes from a string.
- Use `strip_attr:("class, id")` to keep specific attributes.
- Example: `"Content"|strip_attr:("class")` returns `Content`.
### `strip_md`
Removes **all** Markdown formatting and returns a plain text string, e.g. turning `**text**` into `text`.
- Turns formatted text into unformatted plain text, including bold, italic, highlights, headers, code, blockquotes, tables, task lists, and wikilinks.
- Entirely removes tables, footnotes, images, and HTML elements.
### `strip_tags`
Removes **all** HTML tags from a string. Content within the tag is preserved.
- Use `strip_tags:("p,strong,em")` to keep specific tags.
- Example: `"Hello **world**!"|strip_tags:("b")` returns `Hello **world**!`.
## Arrays and objects
Process arrays and objects.
### `first`
Returns the first element of an array as a string.
- `["a","b","c"]|first` returns `"a"`.
- If the input is not an array, it returns the input unchanged.
### `join`
Combines elements of an array into a string.
- `["a","b","c"]|join` returns `"a,b,c"`.
- A custom separator can be specified: `["a","b","c"]|join:" "` returns `"a b c"`. Use `join:"\n"` to separate elements with a line break.
- It can be useful after `split` or `slice`: `"a,b,c,d"|split:","|slice:1,3|join:" "` returns `"b c"`.
### `last`
Returns the last element of an array as a string.
- `["a","b","c"]|last` returns `"c"`.
- If the input is not an array, it returns the input unchanged.
### `map`
Applies a transformation to each element of an array using the syntax `map:item => item.property` or `map:item => item.nested.property` for nested properties.
- `[{gem: "obsidian", color: "black"}, {gem: "amethyst", color: "purple"}]|map:item => item.gem` returns `["obsidian", "amethyst"]`.
- Use parentheses for object literals and complex expressions: `map:item => ({key: value})`, e.g.: `[{gem: "obsidian", color: "black"}, {gem: "amethyst", color: "purple"}]|map:item => ({name: item.gem, color: item.color})` returns `[{name: "obsidian", color: "black"}, {name: "amethyst", color: "purple"}]`.
String literals are also supported, e.g. `["rock", "pop"]|map:item => "genres/${item}"` returns `["genres/rock", "genres/pop"]`.
Combine `map` with the `template` filter, e.g. `map:item => ({name: ${item.gem}, color: item.color})|template:"- ${name} is ${color}\n"`. For string literal maps, use `${str}` in the template, e.g. `["rock", "pop"]|map:item => "genres/${item}"|template:"- ${str}"`.
Note: Built-in filters cannot be used inside `map`. This means that, for example, trimming each value of an array cannot be done with `map`.
### `merge`
Adds new values to an array.
- For arrays: `["a","b"]|merge:("c","d")` returns `["a","b","c","d"]`.
- Single value: `["a","b"]|merge:"c"` returns `["a","b","c"]`.
- If input is not an array, it creates a new array: `"a"|merge:("b","c")` returns `["a","b","c"]`.
- Values can be quoted: `["a"]|merge:('b,"c,d",e')` returns `["a","b","c,d","e"]`.
### `nth`
Keeps nth items in an array using CSS-style nth-child syntax and group patterns. All positions are 1-based (first item is position 1).
- `array|nth:3` keeps only the 3rd element.
- `array|nth:3n` keeps every 3rd element (3, 6, 9, etc.).
- `array|nth:n+3` keeps the 3rd and all following elements.
Group pattern syntax for repeating structures:
- `array|nth:1,2,3:5` keeps positions 1, 2, 3 from each group of 5 items. Example: `[1,2,3,4,5,6,7,8,9,10]|nth:1,2,3:5` returns `[1,2,3,6,7,8]`.
### `object`
Manipulates object data:
- `object:array` converts an object to an array of key-value pairs.
- `object:keys` returns an array of the object's keys.
- `object:values` returns an array of the object's values.
- Example: `{"a":1,"b":2}|object:array` returns `[["a",1],["b",2]]`.
### `slice`
Extracts a portion of a string or array.
- For strings: `"hello"|slice:1,4` returns `"ell"`.
- For arrays: `["a","b","c","d"]|slice:1,3` returns `["b","c"]`.
- If only one parameter is provided, it slices from that index to the end: `"hello"|slice:2` returns `"llo"`.
- Negative indices count from the end: `"hello"|slice:-3` returns `"llo"`.
- The second parameter is exclusive: `"hello"|slice:1,4` includes characters at indices 1, 2, and 3.
- Using a negative second parameter excludes elements from the end: `"hello"|slice:0,-2` returns `"hel"`.
### `split`
Divides a string into an array of substrings.
- `"a,b,c"|split:","` returns `["a","b","c"]`.
- `"hello world"|split:" "` returns `["hello","world"]`.
- If no separator is provided, it splits on every character: `"hello"|split` returns `["h","e","l","l","o"]`.
- Regular expressions can be used as separators: `"a1b2c3"|split:[0-9]` returns `["a","b","c"]`.
### `template`
Applies a template string to an object or array of objects, using the syntax `object|template:"Template with ${variable}"`.
- Access nested properties: `{"gem":{"name":"Obsidian"}}|template:"${gem.name}"` returns `"Obsidian"`.
- For objects: `{"gem":"obsidian","hardness":5}|template:"${gem} has a hardness of ${hardness}"` returns `"obsidian has a hardness of 5"`.
- For arrays: `[{"gem":"obsidian","hardness":5},{"gem":"amethyst","hardness":7}]|template:"- ${gem} has a hardness of ${hardness}\n"` returns a formatted list.
Works with string literals from `map` using `${str}`:
- Example: `["rock", "pop"]|map:item => "genres/${item}"|template:"- ${str}"` returns a formatted list.
### `unique`
Removes duplicate values from arrays and objects.
- For arrays of primitives: `[1,2,2,3,3]|unique` returns `[1,2,3]`.
- For arrays of objects: `[{"a":1},{"b":2},{"a":1}]|unique` returns `[{"a":1},{"b":2}]`.
- For objects it removes properties with duplicate values, keeping the last occurrence's key.
- For strings it returns the input unchanged.
---
## File: docs/Highlight web pages.md
---
permalink: web-clipper/highlight
aliases:
- highlights
- Highlighter
---
[[Introduction to Obsidian Web Clipper|Web Clipper]] lets you highlight text on web pages, and select the elements you want to save to Obsidian. Your highlights are saved, so you can revisit them when you return to a page.
Highlights can be [[Clip web pages|captured]] and saved to Obsidian when you open the extension.
## Turn on highlighter
You can turn on highlighting in several ways, depending on your browser:
- The highlighter icon in the extension panel.
- Hotkeys, to activate the extension from your keyboard.
- Context menu, by right-clicking the web page you are visiting.
Once highlighting is on, you can select text, images, and elements you want to highlight.
## Highlighter settings
You can change the highlighter behavior by going to Web Clipper settings. Here you can also export your highlights to a `.json` file.
There are three options for highlights to be inserted into your clipped note via the `{{content}}` [[Variables|variable]]:
- **Highlight the page content** — adds highlights directly to the text with the [[Obsidian Flavored Markdown|syntax]] `==highlight==`.
- **Replace the page content** — returns a list of highlights, without any of the page content.
- **Do nothing** — returns the original content without highlights.
You can add highlights directly to your template using the `{{highlights}}` variable, for example:
```
{{highlights|map: item => item.text|join:"\n\n"}}
```
---
## File: docs/Interpret web pages.md
---
permalink: web-clipper/interpreter
aliases:
- Interpreter
---
Interpreter is a [[Introduction to Obsidian Web Clipper|Web Clipper]] feature that lets you interact with web pages using natural language. Interpreter helps you capture and modify data that you want to save to Obsidian. For example:
- Extract specific text fragments.
- Summarize or explain information.
- Convert text from one format to another.
- Translate text to a different language.
Interpreter leverages language models to process information on a web page, and return results using [[Variables]] that you can add to your [[Obsidian Web Clipper/Templates|Web Clipper Templates]].
![[web-clipper-interpreter-demo.mp4#interface]]
## Examples of prompts
Prompts use the [[Variables|variable]] syntax `{{"your prompt"}}`. You can use this syntax with any natural language query, e.g.
- `{{"a summary of the page"}}` to extract a summary of the page.
- `{{"a three bullet point summary, translated to French"}}` to extract bullet points about the page, and translate them to French.
- `{{"un resumé de la page en trois points"}}` to extract three bullet points using a prompt in French.
The output of your prompts can be further manipulated using [[Filters]]. Filters are processed after the prompt response is received from the model. For example: `{{"a summary of the page"|blockquote}}` will turn the response into a blockquote.
## Get started
Interpreter works with almost any language model provider, including options that run privately on your device. To set up Interpreter:
1. Go to the **Interpreter** section in Web Clipper settings.
2. Toggle on **Enable Interpreter**.
3. Configure your provider and model, see [[Interpret web pages#Models|models]] section below.
4. Add [[Variables|prompt variables]] to your [[Obsidian Web Clipper/Templates|templates]].
5. If your template includes prompt variables, the Interpreter section will be visible when you [[Clip web pages|clip a page]]. Click **interpret** to process the prompt variables.
## How it works
When Interpreter is enabled *and* your template contains [[Variables#Prompt variables|prompt variables]], a new Interpreter section is displayed in the extension window, above the **Add to Obsidian** button. This section lets you select a model and run Interpreter for the current page.
When you click **interpret**, Interpreter sends the page context to your selected model, along with *all* the prompts in your template in one request. Depending on the model provider you choose, this can be an external call or local to your device. The model evaluates your prompts against the page context, and returns its responses. Interpreter then replaces the prompt variables with the response data.
The whole process can take milliseconds or more than 30 seconds depending on the model you use and the amount of data you are processing.
## Context
The term *context* refers to the page data that Interpreter uses to process prompts. The smaller the context, the faster Interpreter runs.
By default, Interpreter uses the entire page HTML as its context, however this can make prompts slower and more expensive than necessary.
You can override the default context in Interpreter **Advanced settings** and define context per [[Obsidian Web Clipper/Templates|template]].
To define a more targeted context use [[Variables#Selector variables|selector variables]] (or other variable types) to interpret a section of the page. For example, you could use the following selector variable in your template's Interpreter context:
```
{{selectorHtml:#main}}
```
This would only run Interpreter on the `#main` element of a web page, if it exists. [[Filters#HTML processing|HTML processing filters]] like `remove_html`, `strip_tags` and `strip_attr` can be useful to further reduce the context length and speed up processing.
## Models
> [!warning] Privacy
> By using a third-party model provider you agree to their terms and privacy policy. Interpreter requests are sent directly to the provider you choose. Obsidian does not gather or store any data about your requests.
### Preset providers
Interpreter includes several preset providers. To use these providers you need an API key which you can get by logging into your provider's account. You will also need to decide which model(s) to use.
| Provider | API key | Models |
| ------------------ | ----------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| Anthropic | [API key](https://console.anthropic.com/settings/keys) | [Models](https://docs.anthropic.com/en/docs/about-claude/models) |
| Azure OpenAI | [API key](https://oai.azure.com/portal/) | [Models](https://learn.microsoft.com/en-us/azure/ai-services/openai/concepts/models) |
| DeepSeek | [API key](https://platform.deepseek.com/api_keys) | [Models](https://api-docs.deepseek.com/quick_start/pricing) |
| Google Gemini | [API key](https://aistudio.google.com/apikey) | [Models](https://ai.google.dev/gemini-api/docs/models/gemini) |
| Hugging Face | [API key](https://huggingface.co/settings/tokens) | [Models](https://huggingface.co/models?pipeline_tag=text-generation&sort=trending) |
| Meta | [API key](https://llama.developer.meta.com) | [Models](https://llama.developer.meta.com/docs/models) |
| Ollama | n/a | [Models](https://ollama.com/search) |
| OpenAI | [API key](https://platform.openai.com/api-keys) | [Models](https://platform.openai.com/docs/models) |
| OpenRouter | [API key](https://openrouter.ai/settings/keys) | [Models](https://openrouter.ai/models) |
| Perplexity | [API key](https://www.perplexity.ai/settings/api) | [Models](https://docs.perplexity.ai/guides/model-cards) |
| xAI Grok | [API key](https://console.x.ai/team/default/api-keys) | [Models](https://docs.x.ai/docs/models) |
### Choosing a model
In general we recommend using small models with Web Clipper because they are faster and perform fairly accurately for this task. Examples of smaller models include **Anthropic's Claude Haiku**, **Google Gemini Flash**, **Llama** with 3B or 8B parameters, or **OpenAI's Mini** series of models.
### Custom providers and models
To add a custom provider and/or model go to Web Clipper **[[Settings]]** → **Interpreter**:
- **Add provider** to configure preset and custom providers.
- **Add model** to configure preset and custom models.
When adding a custom provider, we recommend that you use their chat completions endpoint for the **Base URL** — it typically ends with `/chat/completions`.
### Local models
Interpreter can use local models which offer greater privacy and offline compatibility. Several options for running local models exist. One of the easiest to configure is Ollama.
#### Ollama
[Ollama](https://ollama.com/) lets you run language models locally and privately on your device.
Once you have downloaded and installed Ollama, add Ollama using **Add provider** in Interpreter settings. Ollama does not require an API key. Then choose a model from the [model list](https://ollama.com/search). For example if you want to use [Llama 3.2](https://ollama.com/library/llama3.2), click **Add model**, then:
- **Provider:** Ollama
- **Display name:** Llama 3.2, this value is customizable.
- **Model ID:** `llama3.2`, this must exactly match the model ID from Olllama.
**Start the Ollama server**
To allow a browser extension to interact with Ollama you must [give it explicit instruction](https://github.com/ollama/ollama/issues/2308) when running the server, or else you will see a `403` error.
Close the Ollama app, and run the following command in your terminal. The protocol should be changed to your browser's extension protocol if you don't use Chrome or Firefox.
```
OLLAMA_ORIGINS=moz-extension://*,chrome-extension://*,safari-web-extension://* ollama serve
```
Then run your model with Ollama the normal way, e.g.
```
ollama run llama3.2
```
**Context length**
Ollama's context window defaults to 2048 tokens. This is the maximum number of tokens for the message and response. When clipping a long web page you can easily exceed this limit. Ollama will silently fail and return irrelevant results. Some options:
- Increase Ollama's `num_ctx` parameter. Be mindful that longer context requires more memory.
- Use the [[#Context]] field in your template to provide a more targeted section of the page, or trim the context using a [[Filters|filter]] e.g. `{{content|slice:0,1000}}`.
---
## File: docs/Introduction to Obsidian Web Clipper.md
---
aliases:
- Web Clipper
- Extending Obsidian/Obsidian Web Clipper
cssclasses:
- list-cards
- list-cards-mobile-full
permalink: web-clipper
---
Obsidian Web Clipper is a free browser extension that lets you highlight pages and save web content to your vault.
> [!warning]- Requires Obsidian 1.7.2 or above
> Web Clipper relies on features added to [[Obsidian URI]] in Obsidian 1.7.2. Please make sure you're using Obsidian 1.7.2 or above.
## Install Web Clipper
Get the Obsidian Web Clipper extension from the official directory for your browser:
- [Chrome Web Store](https://chromewebstore.google.com/detail/obsidian-web-clipper/cnjifjpddelmedmihgijeibhnjfabmlf)
- For Chrome, Brave, Arc, Orion, and other Chromium-based browsers.
- [Firefox Add-Ons](https://addons.mozilla.org/en-US/firefox/addon/web-clipper-obsidian/)
- For Firefox and Firefox Mobile.
- [Safari Extensions](https://apps.apple.com/us/app/obsidian-web-clipper/id6720708363)
- For macOS, iOS, and iPadOS.
- [Edge Add-Ons](https://microsoftedge.microsoft.com/addons/detail/obsidian-web-clipper/eigdjhmgnaaeaonimdklocfekkaanfme)
- For Microsoft Edge.
## How to use Web Clipper
- [[Clip web pages]]
- Get started and learn how to save content from pages.
- [[Highlight web pages|Highlighter]]
- Highlight important passages and select elements you want to save.
- [[Interpret web pages|Interpreter]]
- Use natural language prompts to capture and modify data on a page.
- [[Troubleshoot Web Clipper|Troubleshooting]]
- Resolve common issues with Web Clipper.
- [[Obsidian Web Clipper/Templates|Templates]]
- Create custom templates for specific websites.
- [[Variables]]
- Pre-populate page data in your template.
- [[Filters]]
- Modify the output of template variables.
- [[Logic]]
- Add conditionals and loops to templates.
## Privacy
Obsidian Web Clipper saves content locally to your Obsidian vault and follows our [privacy policy](https://obsidian.md/privacy). Your data is not collected, and we do not gather any usage metrics. The code is [open source](https://github.com/obsidianmd/obsidian-clipper) and auditable.
---
## File: docs/Logic.md
---
permalink: web-clipper/logic
---
[[Introduction to Obsidian Web Clipper|Web Clipper]] supports template logic for conditionals, loops, and variable assignment. This syntax is inspired by [Twig](https://twig.symfony.com/) and [Liquid](https://shopify.github.io/liquid/) templating languages.
## Conditionals
Use `{% if %}` to conditionally include content based on variables or expressions.
```twig
{% if author %}
Author: {{author}}
{% endif %}
```
### Else and elseif
Use `{% else %}` to provide fallback content, and `{% elseif %}` to chain multiple conditions:
```twig
{% if status == "published" %}
Live article
{% elseif status == "draft" %}
Draft article
{% else %}
Unknown status
{% endif %}
```
### Comparison operators
The following comparison operators are supported:
| Operator | Description |
|----------|-------------|
| `==` | Equal to |
| `!=` | Not equal to |
| `>` | Greater than |
| `<` | Less than |
| `>=` | Greater than or equal to |
| `<=` | Less than or equal to |
| `contains` | Check if string contains substring, or array contains value |
Examples:
- `{% if title == "Home" %}` — string equality
- `{% if price >= 100 %}` — numeric comparison
- `{% if title contains "Review" %}` — substring check
- `{% if tags contains "important" %}` — array membership
### Logical operators
Combine conditions using logical operators:
| Operator | Alternative | Description |
|----------|-------------|-------------|
| `and` | `&&` | Both conditions must be true |
| `or` | `\|\|` | At least one condition must be true |
| `not` | `!` | Negates a condition |
Examples:
- `{% if author and published %}` — both must exist
- `{% if draft or archived %}` — either condition
- `{% if not hidden %}` — negation
- `{% if (premium or featured) and published %}` — grouped conditions
Each side of a logical operator must be a complete expression. For example, to check if a string contains one of several values:
```twig
{% if title contains "review" or title contains "opinion" or title contains "editorial" %}
```
### Truthiness
When a variable is used without a comparison operator, it's evaluated for "truthiness":
- `false`, `null`, `undefined`, empty string `""`, and `0` are considered **falsy**
- Empty arrays `[]` are considered **falsy**
- Everything else is **truthy**
```twig
{% if content %}
Has content
{% endif %}
```
## Variable assignment
Use `{% set %}` to create or modify variables within your template:
```twig
{% set slug = title|lower|replace:" ":"-" %}
File: {{slug}}.md
```
Variables can be set to:
- Other variables: `{% set name = author %}`
- Literals: `{% set count = 5 %}` or `{% set label = "Draft" %}`
- Expressions with filters: `{% set excerpt = content|truncate:100 %}`
- Selector results: `{% set comments = selector:.comment %}`
Variables set with `{% set %}` can be used in subsequent template logic and in `{{variable}}` output.
## Fallback values
Use the `??` operator to provide fallback values when a variable is empty or undefined:
```twig
{{title ?? "Untitled"}}
```
If `title` is empty, undefined, or falsy, the fallback value `"Untitled"` will be used instead.
This is a shorthand for the equivalent `if` statement:
```
{% if title %}{{title}}{% else %}Untitled{% endif %}
```
### Chaining fallbacks
You can chain multiple fallbacks:
```twig
{{title ?? headline ?? "No title"}}
```
This will use `title` if available, otherwise `headline`, otherwise the string `"No title"`.
### With filters
Filters bind more tightly than `??`, so filters are applied before the fallback check:
```twig
{{title|upper ?? "UNTITLED"}}
```
This applies `upper` to `title` first, then falls back to `"UNTITLED"` if the result is empty. To apply filters to the fallback value, use parentheses or separate expressions:
```
{{title ?? "Untitled"|lower}}
```
This will use `title` if available, otherwise apply `lower` to the fallback, resulting in `"untitled"`.
## Loops
Use `{% for %}` to iterate over arrays:
```twig
{% for item in schema:author %}
- {{item.name}}
{% endfor %}
```
### Loop sources
You can loop over:
- Schema arrays: `{% for item in schema:author %}`
- Selector results: `{% for comment in selector:.comment %}`
- Variables set earlier: `{% set items = selector:.item %}{% for item in items %}`
### Loop variables
Inside a loop, you have access to a `loop` object with the following properties:
| Variable | Description |
|----------|-------------|
| `loop.index` | Current iteration (1-indexed) |
| `loop.index0` | Current iteration (0-indexed) |
| `loop.first` | `true` if first iteration |
| `loop.last` | `true` if last iteration |
| `loop.length` | Total number of items |
```twig
{% for tag in tags %}
{{loop.index}}. {{tag}}
{% if loop.last %} (end of list){% endif %}
{% endfor %}
```
For backwards compatibility, you can also use `item_index` (where `item` is your iterator variable name) to get the 0-indexed position:
```twig
{% for tag in tags %}
{{tag_index}}. {{tag}}
{% endfor %}
```
### Accessing array items by index
Use bracket notation to access array elements by index:
```twig
{{items[0]}}
{{items[loop.index0]}}
```
This is useful when you need to access items from multiple arrays in parallel:
```twig
{% set transcripts = selector:.transcript-text %}
{% set timestamps = selector:.timestamp %}
{% for line in transcripts %}
{{timestamps[loop.index0]}} - {{line}}
{% endfor %}
```
Bracket notation also works with object properties:
```twig
{{user["name"]}}
{{data["my-key"]}}
```
### Nested loops
Loops can be nested for complex data structures:
```twig
{% for section in sections %}
## {{section.title}}
{% for item in section.items %}
- {{item}}
{% endfor %}
{% endfor %}
```
## Combining logic
Conditionals and loops can be combined:
```twig
{% for item in items %}
{% if item.active %}
- {{item.name}}
{% endif %}
{% endfor %}
```
## Evaluation order
Template logic is processed in the following order:
1. **Template logic** — `{% if %}`, `{% for %}`, `{% set %}`, and `{{variables}}` are evaluated first
2. **Prompt variables** — [[Variables#Prompt variables|Prompt variables]] like `{{"summarize this"|prompt}}` are sent to the Interpreter after template logic is complete
This means you can use template logic to construct prompts dynamically, but prompt results are not available for use in conditionals or loops.
---
## File: docs/Templates.md
---
permalink: web-clipper/templates
---
[[Introduction to Obsidian Web Clipper|Web Clipper]] allows you to create templates that automatically capture and organize metadata from web pages. Example templates are available in the [clipper-templates repo](https://github.com/kepano/clipper-templates).
## Create or edit a template
To **create** a template go to Web Clipper settings and click the **New template** button in the sidebar. You can also **duplicate** a template in the **More** actions menu in the top right corner.
To **edit** a template choose a template from the sidebar. Your changes will be saved automatically.
Templates make use of [[Variables]], [[Filters]], and [[Logic]], which allow you to tailor how content will be saved.
## Import and export Web Clipper templates
To import a template:
1. Open the extension and click the **[[Settings]]** cog icon.
2. Go to any template in the list.
3. Click **Import** in the top right or drag and drop your `.json` template file(s) anywhere in the template area.
To export a template click **Export** in the top right. This will download the template `.json` file. You can also copy the template data to your clipboard via the **More** menu.
## Template settings
### Behavior
Define how content from Web Clipper will be added to Obsidian:
- **Create a new note**
- **Add to an existing note**, at the top or bottom
- **Add to daily note**, at the top or bottom (requires the [[daily notes]] plugin to be active)
### Automatically trigger a template
Template triggers allow you to automatically select a template based on the current page URL or [schema.org](https://schema.org/) data. You can define multiple rules for each template, separated by a new line.
The first match in your template list determines which template is used. You can drag templates up and down in Web Clipper settings to change the order in which templates are matched.
#### Simple URL matching
Simple matching triggers a template if the current page URL *starts with* the given pattern. For example:
- `https://obsidian.md` will match any URL that starts with this text.
#### Regular expression matching
You can trigger templates based on more complex URL patterns using regular expressions. Enclose your regex pattern in forward slashes (`/`). Remember to escape special characters in regex patterns (like `.` and `/`) with a backslash (`\`). For example:
- `/^https:\/\/www\.imdb\.com\/title\/tt\d+\/reference\/?$/` will match any IMDB reference page.
#### Schema.org matching
You can trigger templates based on [schema.org](https://schema.org/) data present on the page. Use the `schema:` prefix followed by the schema key you want to match. You can optionally specify an expected value. For example:
- `schema:@Recipe` will match pages where the schema type is "Recipe".
- `schema:@Recipe.name` will match pages where `@Recipe.name` is present.
- `schema:@Recipe.name=Cookie` will match pages where `@Recipe.name` is "Cookie".
Schema.org values can also be used to [[Variables#Schema.org variables|pre-populate data in templates]].
### Interpreter context
When [[Interpret web pages|Interpreter]] is enabled, you can use [[Variables#Prompt variables|prompt variables]] to extract page content with natural language. For each template you can define the [[Interpret web pages#Context|context]] that Interpreter has access too.
---
## File: docs/Troubleshoot Web Clipper.md
---
permalink: web-clipper/troubleshoot
---
If you encounter issues with [[Introduction to Obsidian Web Clipper|Web Clipper]] you can get help via the [official Discord channel](https://discord.com/channels/686053708261228577/1285652864089198672). You can also report bugs on the [GitHub repo](https://github.com/obsidianmd/obsidian-clipper).
## General
### Some content is missing
By default, Web Clipper tries to intelligently capture content from the page. However it may not be successful in doing so across all websites.
Web Clipper uses [Defuddle](https://github.com/kepano/defuddle) to capture only the main content of the page. This excludes header, footer, and other elements, but sometimes it can be overly conservative and remove content that you want to keep. You can [report bugs](https://github.com/kepano/defuddle) to Defuddle.
To bypass Defuddle in Web Clipper use the following methods:
- Select text, or use `Cmd/Ctrl+A` to select all text.
- [[Highlight web pages|Highlight content]] to choose exactly what you want to capture.
- Use a [[Obsidian Web Clipper/Templates|custom template]] for the site.
### No content appears in Obsidian
If you don't see any content in Obsidian when you click **Add to Obsidian**:
- Check for errors in the Obsidian [[Help and support#Capture console logs|developer console]].
- Check that your vault name in Web Clipper settings exactly matches your *vault name* in Obsidian *not the vault path*.
- Check that the folder name is correctly formatted.
## Linux
#### Obsidian does not open
- Make sure the [[Obsidian URI]] protocol [[Obsidian URI#Register Obsidian URI|is registered]].
- If you are using Firefox you may need to [register it the browser settings](https://kb.mozillazine.org/Register_protocol).
#### Obsidian opens but only the file name is saved
It is likely that Obsidian cannot access your clipboard. Clipboard access is necessary to pass data from your browser to Obsidian. Your configuration can affect how apps are sandboxed, and clipboard permissions.
If you use Wayland, make sure that Obsidian has the permissions to read the clipboard when the app is not focused. For example, in your Hyprland configuration:
```ini
# hyprland.conf
misc {
focus_on_activate = true
}
```
- If you use Flatpak consider trying an [officially supported Obsidian version](https://obsidian.md/download).
- As a fallback, try switching to **Legacy mode** in **Web Clipper Settings** → **General**. This will bypass the clipboard and save content directly via URI. Note that this will limit the number of characters that can be clipped depending on your browser and Linux distribution.
## iOS and iPadOS
To enable the Web Clipper extension for Safari:
1. Go to Safari, tap the leftmost button in the browser URL bar, it looks like a rectangle with lines beneath it.
2. Tap **Manage Extensions**.
3. Enable **Obsidian Web Clipper** in the Extensions list.
4. Exit the menu.
5. To use the extension **tap the puzzle piece icon** in the URL bar.
To allow Web Clipper to run on all websites:
1. Go to iOS **[[Settings]]** → **Apps** → **Safari** → **Extensions**.
2. Under **Permissions** allow it to run on all websites.
To allow Obsidian to always receive Web Clipper content:
1. Go to iOS **[[Settings]]** → **Apps** → **Obsidian**.
2. Set **Paste from other apps** to **Allow**.
---
## File: docs/Variables.md
---
permalink: web-clipper/variables
---
[[Obsidian Web Clipper/Templates|Web Clipper templates]] can use variables to automatically pre-populate data from the page in a template. Variables can be used in the **note name**, **note location**, **properties**, and **note content**. Variables can also be modified using [[filters]].
Use the `...` icon in the [[Introduction to Obsidian Web Clipper|Web Clipper]] extension to access the current page variables for use in templates. There are five types of variables you can use:
- [[Variables#Preset variables|Preset variables]]
- [[Variables#Prompt variables|Prompt variables]]
- [[Variables#Meta variables|Meta variables]]
- [[Variables#Selector variables|Selector variables]]
- [[Variables#Schema.org variables|Schema.org variables]]
## Preset variables
Preset variables are automatically generated based on the page content. These typically work for most websites.
The main content variable is `{{content}}`, which contains the article content, or the [[Highlight web pages|highlights]], or the selection if there is any selected text on the page. Note that `{{content}}` attempts to extract the main content of the page, which may not always be what you want. In that case, you can use other preset variables or selector variables to extract the content you need.
| Variable | Description |
| ------------------- | -------------------------------------------------------------------------------------- |
| `{{author}}` | Author of the page |
| `{{content}}` | Article content, [[Highlight web pages\|highlights]], or selection, in Markdown format |
| `{{contentHtml}}` | Article content, [[Highlight web pages\|highlights]], or selection, in HTML format |
| `{{date}}` | Current date, can be formatted using the `date` filter |
| `{{description}}` | Description or excerpt |
| `{{domain}}` | Domain |
| `{{favicon}}` | Favicon URL |
| `{{fullHtml}}` | Unprocessed HTML for the full page content |
| `{{highlights}}` | [[Highlight web pages\|Highlights]] with text and timestamps |
| `{{image}}` | Social share image URL |
| `{{published}}` | Published date, can be formatted using the `date` filter |
| `{{selection}}` | Selection in Markdown format |
| `{{selectionHtml}}` | Selection in HTML format |
| `{{site}}` | Site name or publisher |
| `{{title}}` | Title of the page |
| `{{time}}` | Current date and time |
| `{{url}}` | Current URL |
| `{{words}}` | Word count |
## Prompt variables
Prompt variables leverage language models to extract and modify data using natural language. Prompt variables require [[Interpret web pages|Interpreter]] to be enabled and configured.
Prompt variables use the syntax `{{"a summary of the page"}}`. The double quotes around the prompt are important and distinguish prompts from preset variables. Prompt responses can be post-processed with [[filters]], e.g. `{{"a summary of the page"|blockquote}}`.
### When to use prompt variables
Prompt variables have the benefit of being extremely flexible and easy to write, however they come with several tradeoffs: they are slower to run, and may have cost and privacy considerations depending on the [[Interpret web pages#Models|provider]] you choose.
Unlike other variable types, prompt variables need to be processed by an external language model, so they are replaced only once [[Interpret web pages|Interpreter]] has run.
It is best to *not* use prompt variables if the data you want to extract is in a consistent format that could be extracted with other variable types.
On the other hand, prompt variables can be useful if the data you want to extract is an *inconsistent* format across websites. For example, you can make a [[Obsidian Web Clipper/Templates|template]] to save books that is agnostic of the book site. Prompt variables like `{{"author of the book"}}` will work across any book site, whereas selector variables typically only work for one site.
### Examples
Prompts can use almost any natural language query. Depending on the model you use, prompts can query or translate data across languages.
- `{{"a three bullet point summary, translated to French"}}` to extract bullet points about the page, and translate them to French.
- `{{"un resumé de la page en trois points"}}` to extract three bullet points using a prompt in French.
Prompts can transform page content into JSON that can be manipulated with [[Filters|filters]]. For example:
```
{{"return a JSON object for each tweet, that includes the author, tweet_text, date in YYYY-MM-DD format, and images array (if there are any)"|map:tweet => ({text: tweet.tweet_text, author: tweet.author, date: tweet.date})|template:"${text}\n— [[@${author}]], [[${date}]]\n"}}
```
## Meta variables
Meta variables allow you to extract data from [meta elements](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta) in the page, including [Open Graph](https://ogp.me/) data used to populate social share previews.
- `{{meta:name}}` returns the content of the meta name tag with the given name, e.g. `{{meta:name:description}}` for the `description` meta tag.
- `{{meta:property}}` returns the content of the meta property tag with the given property, e.g. `{{meta:property:og:title}}` for the `og:title` meta tag.
## Selector variables
Selector variables allow you to extract text content from elements on the page using [CSS selectors](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_selectors/Selectors_and_combinators).
The syntax is `{{selector:cssSelector?attribute}}`, where `?attribute` is optional. If no attribute is specified, the text content of the element is returned. You can also use `{{selectorHtml:cssSelector}}` to get the HTML content of the element. Selector variables tend to work best on a specific website or set of websites that have consistent HTML structure.
- `{{selector:h1}}` returns text content of any `h1` elements on the page.
- `{{selector:.author}}` returns text content of any `.author` elements on the page.
- `{{selector:img.hero?src}}` returns the `src` attribute of the image with class `hero`.
- `{{selector:a.main-link?href}}` returns the `href` attribute of the anchor tag with class `main-link`.
- `{{selectorHtml:body|markdown}}` returns the entire HTML of the `body` element, converted to Markdown using the `markdown` [[Filters#HTML processing|filter]].
- Nested CSS selectors and combinators are supported if you need more specificity.
- If multiple elements match the selector, an array is returned, which you can process with [[Filters#Arrays and objects|array and object filters]] like `join` or `map`.
Selector variables can also be used directly in [[Templates#Template logic|template logic]]:
- In loops: `{% for comment in selector:.comment %}...{% endfor %}`
- In conditionals: `{% if selector:.premium-badge %}...{% endif %}`
- In variable assignment: `{% set items = selector:.list-item %}`
## Schema.org variables
Schema variables allow you to extract data from [schema.org](https://schema.org/) JSON-LD on the page. Schema.org data can also be used to automatically [[Obsidian Web Clipper/Templates#Schema.org matching|trigger a template]].
- `{{schema:@Type:key}}` returns the value of the key from the schema.
- `{{schema:@Type:parent.child}}` returns the value of a nested property.
- `{{schema:@Type:arrayKey}}` returns the first item in an array.
- `{{schema:@Type:arrayKey[index].property}}` returns the item at the specified index in an array.
- `{{schema:@Type:arrayKey[*].property}}` returns a specific property from all items in an array.
You can also use a shorthand notation without specifying the schema type:
- `{{schema:author}}` will match the first `author` property found in any schema type.
- `{{schema:name}}` will match the first `name` property found in any schema type.
This shorthand is particularly useful when you don't know or don't care about the specific schema type, but you know the property name you're looking for.
Nested properties and array access work as well, both with and without the schema `@Type` specified:
- `{{schema:author.name}}` will find the first `author` property and then access its `name` sub-property.
- `{{schema:author[0].name}}` will access the `name` of the first author in an array of authors.
- `{{schema:author[*].name}}` will return an array of all author names.