Plugin: Google Docs Sync - Bidirectional sync with images, OAuth wizard, and proper distribution

Following up on earlier discussion in the features thread and @Eduardo's suggestion to create a dedicated thread.

What is it?

A plugin that provides bidirectional sync between Joplin notes and Google Docs — including images. Edit in Joplin, changes appear in Google Docs. Collaborate in Google Docs, pull changes back to Joplin.

Repository: GitHub - joplin-plugin-google-docs


Why this exists

Teams with technical knowledge scattered in Markdown across dev machines, but company-mandated Google Workspace for official documentation. This bridges that gap — your notes become shareable, collaborative documents without leaving your Joplin workflow.


Current State

This is no longer a proof-of-concept. The plugin has been through a complete migration to the official Joplin plugin template and is distributed as a proper .jpl file (639KB, all dependencies bundled via webpack tree-shaking). The latest progress can be foud in the feat/official-template-migration branch.

What works:

Feature Status
Export note → Google Doc (with full Markdown formatting) :white_check_mark:
Import Google Doc → Joplin note :white_check_mark:
Export entire notebook → Google Drive folder :white_check_mark:
Push/Pull sync with conflict detection :white_check_mark:
Image sync (including WebP, AVIF auto-conversion) :white_check_mark:
Background polling for remote changes :white_check_mark:
Sync status icons in note list :white_check_mark:
Setup wizard (OAuth configuration guidance) :white_check_mark:
Bind/unbind notes to existing Docs :white_check_mark:

Markdown conversion: headings, bold/italic, strikethrough, inline code, fenced code blocks (with language detection), links, ordered/unordered lists, blockquotes, horizontal rules, and images.


The image sync story

Getting images to work was... instructive. Google Workspace domain policies can block Drive sharing even when the API reports success. WebP images (common from web clipping) aren't supported by the Docs API. Joplin's plugin sandbox streams behave differently than standard Node.js.

The solution: upload to Google Cloud Storage using direct HTTPS requests (bypassing googleapis stream issues), convert unsupported formats to PNG using Chromium's Canvas API (no external dependencies — Electron gives us browser-native WebP/AVIF decoding for free), and revoke public access immediately after the Docs API fetches the image.

The full debugging journey is documented in the Image Sync Implementation wiki page — might be useful if you're building something similar.


Technical notes

Architecture: Provider-agnostic design. Google Docs is the first backend, but the interfaces support adding other formats (DOCX local export is stubbed).

Sync mechanism: Delta sync via Drive Changes API (only processes changed items), optimistic concurrency using requiredRevisionId checks.

Build: Migrated from custom TypeScript build to official Joplin plugin template. The Webpack Migration wiki documents the complete journey — including the discovery that Joplin's plugin sandbox doesn't extract node_modules from .jpl archives, so static imports with full webpack bundling is the only reliable approach.


Setup

You'll need a Google Cloud project with Docs, Drive, and Storage APIs enabled. The plugin includes a setup wizard that walks through OAuth configuration. The project wiki has 15 pages of documentation covering architecture, implementation details, and troubleshooting.

Scopes required:

  • drive.file — Only files created by the plugin or explicitly selected
  • documents — Read/write Google Docs
  • devstorage.full_control — Temporary image upload for embedding (optional, only if you want image sync)

What's next

  • Image pull (round-trip images back from Docs to Joplin)
  • Tab-based sync (notebook → one Doc, notes → tabs within)
  • Submission to the official plugin repository

Caveats

I use this daily with my primary Joplin instance. That said:

  • Some edge cases in Markdown conversion may not be perfect
  • Conflict resolution is basic (last-writer-wins with dialogs)
  • This is developed with AI assistance (Cursor + Claude) — the code has gone through multiple refactoring passes but may still have naming quirks

Feedback, bug reports, and contributions welcome. Particularly interested in edge cases people encounter with Markdown formatting or Workspace configurations.

2 Likes

Looks like I’m a bit late, you’ve got everything implemented. Is there any issues or current feature you have in mind ?

Regarding your basic sync: last-writer-win. Is it issue only for push operation? For pull operation, delta sync works fine, correct?

You haven't come late since I haven't yet merged the migrated plugin to master.

Nor have I fixed all the formatting to my liking and I also haven't implemented some features, like numbered lists.

I also started working on syncing customised css into Google docs (I use callout boxes in my notes), but haven't found a way to abstract the concept sufficiently, so that it is configurable on the user side.

I will try to take some time aside to merge to master (or just revise and force push) this weekend.

Then I need:

  1. Run unit tests to make sure conversion works reliably.
  2. Have a lot of real user testing with polling enabled in order to make sure things work reliably I'm general.

After that I will publish it to plugins.

However, the numbered lists and customised configurable css abstractions seem like the way to go for immediate development.

As for longer future, docx or odf sync for Collabora or other collaborative Office suites makes sense,

In general, if this plugin is to live long term, I must make sure everything is abstracted enough to be able to make it universal for variety of office suites or collaborative tools.

1 Like

Hello everyone, I have been quiet in here and very busy at work. I did find time to refactor the plugin to follow the recommended Joplin style and tried to include all other @laurent’s GSoC suggestions about AI attributions.

Now all the changes have been merged to master and I published the plugin to npm and hence it is downloadable at: Joplin Plugins - Google Docs Sync

I need to fix the README in the npm (not relevant to users) and fix the example images, but other than that, you are all free to test it at your discretion.

I am well aware that the plugin requires setting up a GCP project, oAuth2 and a bucket for image syncing, I would love if someone produced easy to follow user guides and sent a pull request.

Remember, I work on the plugin in my free time, so yesterday's release was a late night work.