Using a Google Doc to document internal processes (or external product docs, if you’re really daring) has the big plus of familiarity.

 

Most everything else, IMO, is a minus  — which is why docs-focused platforms exist! But one minus you can at least work around is that images embedded in a Doc won’t auto-update, even if they were loaded from a remote URL.

 

That’s right, all 6 options here for Insert » Image result in a local copy of the image:

 

Even the promising By URL doesn’t work like an HTML <img src> where updates to the remote file are picked up on reload. Instead, it just copies the file into the document that one time. To pick up changes, you need to delete the image and Insert again.

 

But there’s a better way via a bit of Apps (Java)Script.

 

Here’s the key to making the code work. When you insert an image, also set its link to the image’s original URL. Here, I used Insert » By URL to grab the screenshot https://blog.teknkl.com/content/images/example.png. Then I click Insert link and set the same value https://blog.teknkl.com/content/images/example.png.[1]

Now the code knows where to refresh the image from. We’ve gotta use the standard link attribute (LINK_URL in Apps Script) because you can’t add arbitrary attributes to an InlineImage element. (N.B. I’m no fan of Google Docs because of this lack of extensibility.)

 

Get the code

The Apps Script function uses the DOM-like API to (a) get every inline image[2] in the document, (b) fetch the most recent version from the link URL, and (c) swap the images at the same document position:

function refreshImages() {
  const doc = DocumentApp.getActiveDocument();
  const body = doc.getBody();
  const images = body.getImages();

  for( const originalImage of images ){
    const pseudoSource = originalImage.getAttributes().LINK_URL;
    
    if(pseudoSource) {
      const parentEl = originalImage.getParent();
      const imagePosition = parentEl.getChildIndex(originalImage);

      const resp = UrlFetchApp.fetch(pseudoSource);
      const newImageBlob = resp.getBlob(); 
      const newImage = parentEl.insertInlineImage(imagePosition,newImageBlob);

      newImage.setAttributes(originalImage.getAttributes());
      originalImage.removeFromParent();
    }
  }
}

 

Set refreshImages to run On open, which includes refreshing the browser tab:

 

That’s it! Now it’s easier to keep screenshots, logos, etc. up to date.

 
Notes

[1] Yes, this means the image can’t navigate to a web page on click, since it links to its own source URL. A worthy tradeoff, in my opinion. But if you really need to link elsewhere, you could put the URL in the image title instead of the link (some format like <friendly title> | <url>) and use getAltTitle() + split() to parse it out.

 

[2] Extending the code to PositionedImage elements is left as an exercise for the reader unless I get to it first. 😛