DatafetchPro
    1775971792480_0aiyqb.webp
    Apr 12, 20265 min read49 views

    Download Facebook Photos With One Click

    facebook automation, tampermonkey, userscript, browser automation, photo downloader, chrome extension, web scraping


    The problem with saving Facebook photos

    You open a post. You click the image. You wait for it to load in the lightbox. You right-click, find the save option, choose a folder, and confirm. Then you do it again for the next one.

    Each step takes two or three seconds. Do that forty times in an afternoon — pulling reference images for a client deck, archiving posts for a report, or collecting visual assets for a project — and you have burned through twenty minutes on a task that should take none of your attention.

    This is exactly the kind of friction that a userscript is built to remove. A userscript is a small piece of JavaScript that runs inside your browser on pages you choose, adding or changing behaviour without touching the site's own code. The most common way to run one is through a free browser extension called Tampermonkey, which works in Chrome, Edge, Firefox, and most Chromium-based browsers.

    I built a userscript that adds a single "Download Photo" button directly onto each Facebook post. One click saves the image to your downloads folder. No new tab opens. No menu appears. No right-click required.


    What the script actually does

    When you load a Facebook feed or open a specific post, the script scans the page for full-size images attached to posts. It skips profile thumbnails, emoji, reaction icons, and other interface graphics — anything below a size threshold that would clutter your downloads with junk you don't want.

    For every qualifying image it finds, it injects a small download button styled to sit cleanly inside the post card. The button uses Facebook's own design tokens — the same font, the same corner radius — so it looks like it belongs there rather than bolted on from outside.

    Because Facebook loads content dynamically as you scroll (meaning posts appear without the page fully reloading), the script uses a MutationObserver — a browser API that watches for new content appearing in the DOM and triggers the button injection each time new posts load. This is what makes it reliable on an infinite-scroll feed rather than just on the first screenful of posts.

    The result: scroll your feed normally, and every post image already has a download button waiting.


    Who this is actually useful for

    If you save one or two images a month, the manual process is fine. This script earns its keep when volume is the problem.

    The people I see it helping most:

    Social media managers and researchers pulling visual references, competitive screenshots, or post archives across multiple accounts or pages. The manual process doesn't just take time — it breaks concentration.

    Operations and marketing teams who maintain brand asset libraries or document campaign performance with image evidence. Having to right-click and save forty images before a weekly report is a small but consistent drain.

    Anyone who collects visual inspiration — mood boards, design references, photography — and finds the friction of the native Facebook interface slows down a habit that would otherwise be automatic.

    If you recognise your own workflow in any of these, the script removes a step you currently do on autopilot and gives that attention back.


    How Tampermonkey fits into browser automation

    This script is one end of a spectrum. At the lightweight end, Tampermonkey userscripts handle single-page enhancements — adding a button, reformatting a table, intercepting a link. They require no server, no scheduled job, and no infrastructure. You install the extension, paste the script, and it runs every time you visit the matching URL.

    At the heavier end, tools like Playwright (a Python library for controlling a full browser programmatically) handle multi-step workflows: logging in, navigating between pages, extracting structured data, and saving it to a spreadsheet or database. The Playwright documentation gives a clear picture of what's possible when you need automation that runs on a schedule rather than on demand.

    Tampermonkey and Playwright are not competing tools — they solve different problems. The userscript approach is right when a human is already sitting at the browser and just wants one action to be faster. Playwright is right when the whole workflow should run without anyone watching.

    For a more technical comparison of how browser-based automation fits alongside API-based data collection, the MDN Web Docs entry on MutationObserver explains the underlying mechanism this script uses — and why it matters for dynamic pages specifically.


    A note on terms of service

    Automating interactions with Facebook — even something as simple as triggering a download — touches on the platform's terms of service and its robots.txt file. Whether a given use of this script is permitted under those terms is a decision you need to make based on your own situation and, if necessary, legal advice. I can build the tool; the compliance review is yours to run. The Facebook Terms of Service and the robots.txt standard are the two documents worth reading before deploying any browser automation against a major platform.


    What I focused on when building it

    The brief I gave myself was: make it invisible until you need it, and make it work every time.

    That meant three specific constraints.

    Weight. The script adds no external libraries, no tracking, and no network calls beyond the download itself. It loads in milliseconds and does nothing when you are not on a Facebook post.

    Reliability on dynamic pages. A naive version of this script would inject buttons on page load and stop. Facebook's feed keeps arriving as you scroll, so the MutationObserver is the piece that makes it work past the first screen — it re-runs the injection logic whenever new post content appears in the DOM.

    Visual fit. A button that looks foreign breaks the experience and draws attention to the automation in a way that a native-looking control does not. The styling borrows directly from Facebook's existing component classes where possible.

    There was no goal to build something complex. The goal was to remove one specific piece of friction — and to have it stay removed even as Facebook updates its feed layout.


    If you need something similar built for your workflow

    This script handles a specific case: one-click downloads from a Facebook feed. If your situation is different — a different platform, a different kind of data, or a process that runs on a schedule rather than on demand — the underlying approach scales further than a single userscript.

    I build custom browser automation tools for business owners and teams who need data or actions out of websites they can't access through an API. Depending on the task, that might be a Tampermonkey script like this one, a full Playwright bot that runs overnight, or a Chrome extension that sits in your toolbar. If you want to talk through what your workflow actually needs, the hire me page has the details on how I work and what to send me.

    For context on the range of things browser automation can handle, the blogs section has more worked examples across different platforms and use cases.

    JavaScript
    // ==UserScript==
    // @name FB Universal Photo Downloader
    // @namespace http://tampermonkey.net/
    // @version 1.4
    // @description Professional download button that follows the image
    // @author Ali
    // @match https://web.facebook.com/*
    // @grant GM_download
    // ==/UserScript==

    (function() {
    'use strict';

    const BTN_ID = 'ali-fb-dl-btn';

    // 1. Professional Styling (FB Design System)
    const injectStyles = () => {
    if (document.getElementById('ali-styles')) return;
    const style = document.createElement('style');
    style.id = 'ali-styles';
    style.innerHTML = `
    .${BTN_ID} {
    background-color: #0866FF;
    color: white;
    border: none;
    padding: 7px 14px;
    margin: 10px;
    border-radius: 6px;
    cursor: pointer;
    font-family: "Segoe UI", Helvetica, Arial, sans-serif;
    font-size: 13px;
    font-weight: 600;
    display: inline-flex;
    align-items: center;
    z-index: 999;
    transition: background 0.2s;
    }
    .${BTN_ID}:hover { background-color: #0552D1; }
    .${BTN_ID} svg { margin-right: 6px; fill: white; }
    `;
    document.head.appendChild(style);
    };

    const downloadHandler = (imgSrc) => {
    if (!imgSrc) return;
    GM_download({
    url: imgSrc,
    name: `FB_Image_${Date.now()}.jpg`,
    saveAs: true
    });
    };

    const processPosts = () => {
    // Find all images that look like post content (usually have 'scontent' in URL)
    const allImages = document.querySelectorAll('img[src*="scontent"]');

    allImages.forEach(img => {
    // Filter out profile pictures and small icons
    if (img.width < 300) return;

    // Find the closest common container for a post (x1lliihq is the most reliable "block")
    const postContainer = img.closest('.x1yzt60w') || img.closest('.x1lliihq');
    if (!postContainer) return;

    // Strict Check: Don't add if button exists in this container
    if (postContainer.querySelector(`.${BTN_ID}`)) return;

    const btn = document.createElement('button');
    btn.className = BTN_ID;
    btn.innerHTML = `
    <svg width="14" height="14" viewBox="0 0 20 20">
    <path d="M10 2v8.586L12.293 8.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 111.414-1.414L9 10.586V2a1 1 0 112 0zm-7 13a1 1 0 011-1h12a1 1 0 110 2H4a1 1 0 01-1-1z"/>
    </svg>
    Download Photo
    `;

    btn.onclick = (e) => {
    e.preventDefault();
    e.stopPropagation();
    downloadHandler(img.src);
    };

    // Insert at the top of the post for guaranteed visibility
    postContainer.prepend(btn);
    });
    };

    injectStyles();

    // Check every 2 seconds (More reliable for FB's heavy React updates)
    setInterval(processPosts, 2000);
    })();
    0

    Rather not build it yourself?

    I build this kind of thing for a living.

    Send me the site and what you need out of it — you'll get an approach, a timeline, and a fixed quote back. Scoping is free.