Skip to content

Instantly share code, notes, and snippets.

@ohnit
Last active May 27, 2025 17:35
Show Gist options
  • Save ohnit/c74176ea03119ca8649ded529199088f to your computer and use it in GitHub Desktop.
Save ohnit/c74176ea03119ca8649ded529199088f to your computer and use it in GitHub Desktop.
Scrape comments from Figma
/**
For when you only have 'viewer' access to a Figma project, but you'd really like a csv with all the top-level comments. Replies are not included.
The csv columns will be: "Identifier", "Author", "Message"
## To use:
Paste this code into your browser's devtools.
Creater a new scraper:
```js
let scraper = new FigmaCommentScraper();
```
Then slowly scroll through all the comments. When you are done, call:
```js
scraper.stop();
```
You can output the CSV string to the console using either of these:
```js
scraper.dump();
// Doesn't dump comments with ids less than 3.
scraper.dumpAfter(3);
```
You can copy and paste into the a spreadsheet. You may need to specify "text to columns"
I had a problem with newlines in comments. I used this formula. Then pasted as values back into the original column:
`=SUBSTITUTE(D67,"\n", CHAR(10))`
*/
class FigmaCommentScraper {
allComments;
#interval;
constructor() {
this.allComments = {};
this.scrape();
this.start();
}
/**
Collects comments are are visible in the sidebar. To get all comments, you need to scroll. (see `start()`)
*/
scrape() {
Array.from(document.querySelectorAll("div[class^=comments_row_presentation--commentInner]")).map((elem) => {
const message = elem.querySelector(':scope div[class^=comment_message--commentMessage]')?.innerText;
const author = elem.querySelector(':scope div[class^=comment_metadata--author]')?.innerText;
const identifier = elem.querySelector(':scope span[class^=comment_metadata--parentName]')?.innerText.replace("#", "").split(" ")[0];
// console.log(message, author, identifier);
if (identifier in this.allComments === false) {
this.allComments[identifier] = [identifier, author, message];
}
})
}
/**
Scrapes comments periodically so you can scroll through all the comments.
*/
start() {
this.#interval = setInterval(() => {
this.scrape();
console.log("scraping");
}, 500);
}
/**
Stops periodic scraping.
*/
stop() {
clearInterval(this.#interval);
}
/**
Clear all scraped comments
*/
clear() {
this.allComments = {};
}
/**
Returns comments formatted as CSV.
*/
dump() {
return this.#doDump(this.allComments);
}
/**
Like `dump()` but will skip any comments with an id number less than `identifier`.
This is useful adding new comments to your previous scraped ones.
*/
dumpAfter(identifier) {
const comments = Object.fromEntries(
Object.entries(this.allComments).filter(([key, _value]) => Number(key) > identifier)
);
return this.#doDump(comments);
}
#doDump(comments) {
let lines = Object.values(comments).map(([identifier, author, message]) => {
// don't quote identifier so it gets treated as a number in spreadsheets
return `${identifier},"${this.#escapeCSVQuotes(author)}","${this.#escapeCSVQuotes(message)}"`
});
lines.unshift(`"Identifier", "Author", "Message"`);
return lines.join("\n");;
}
#escapeCSVQuotes(text) {
return text.replaceAll(`"`, `""`);
}
}
let scraper = new FigmaCommentScraper();
// scraper.stop();
// scraper.dump();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment