惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
MyScale Blog
MyScale Blog
博客园 - Franky
The Cloudflare Blog
IT之家
IT之家
Blog — PlanetScale
Blog — PlanetScale
博客园 - 聂微东
WordPress大学
WordPress大学
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
T
The Blog of Author Tim Ferriss
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
罗磊的独立博客
Google DeepMind News
Google DeepMind News
P
Proofpoint News Feed
Martin Fowler
Martin Fowler
aimingoo的专栏
aimingoo的专栏
J
Java Code Geeks
腾讯CDC
雷峰网
雷峰网
Microsoft Azure Blog
Microsoft Azure Blog
G
Google Developers Blog
博客园 - 【当耐特】
美团技术团队
云风的 BLOG
云风的 BLOG

Giant Robots Smashing Into Other Giant Robots

Client success starts before kickoff 5 easy, actionable tips for software development in healthcare Announcing importmap-update: automated dependency updates for importmap-rails When to vibe code an app and when to hire someone Why leaders have to know about PMS 🩸 Is AI ruining my brain? Tech Leaders Meetup is coming to Edinburgh Designers already think in React Tech Leaders Meetups are back in London this autumn GPT and Claude go to heraldry school Inserting State Transitions in Postgres Don’t hire thoughtbot to write code AI makes creating software faster, but in regulated industries, judgment matters more Tech Leaders meetup in Amsterdam PMs Don't Need to Code, but They Do Need to Understand How healthcare tech teams innovate while balancing speed and security Can’t touch the DOM? Reach for :has() to style any element Buying Time, Choosing Words: Consulting Through Diplomatic Communication thoughtbot around the world, meet us at upcoming events Modeling State Transitions in Postgres Humid 1.0: React server-side rendering in Rails can be easy! A prototype is not a product. It's a conversation. New: The State of Software Delivery in Healthcare Sign in with Google for React Native What founders told us about working with AI tools for startups Join us: Building Secure Healthcare Systems Upcase has retired, but the learning continues The Bike Shed Ep 506: The Muppet Software Team Migrating to native stack navigation, with a surprise from iOS 26 Past and present thoughtbotters at LRUG this Monday
How I Built a Chrome Extension Wrapper (and Everything Th...
Valeria Graffeo · 2026-06-09 · via Giant Robots Smashing Into Other Giant Robots

So one day a client came to me and said: “we want a Chrome extension.” Simple enough, right?

First thing to know: the client didn’t want to build a real extension from scratch with its own codebase and logic. They wanted a wrapper, essentially their existing Rails app, dressed up and living inside a Chrome extension panel.

Same features, same views, same assets. Just… accessible from the browser bar.

That sounds simpler than building from scratch. But, was it?

Exploring options

Now, when it comes to Chrome extensions, there are a few main UI patterns:

  • Side panel: slides in like a drawer on the right side of the screen
  • Popup: the little window that appears when you click the extension icon in Chrome’s toolbar
  • Iframe overlay: inject an iframe into the current page, absolutely positioned wherever you want

The trigger can be a button injected into the page (via content_scripts, positioned with CSS), or the native Chrome extension icon in the browser bar. We explored a few options before eventually landing on a reference to model it after.

We ended up going with an iframe approach: a wrapper around the main Rails app, rendering its views and assets inside the extension panel.

The Stack Problem: Rails Didn’t Like This

Here’s where it gets fun. The code for the extension, the page with the trigger, the button, the iframe, all of it, lived inside the same Rails app that hosted the main product. Why? Because it needed to render views and serve assets from that app, and stay in sync if the main content changed.

But running two things from one Rails app (the main UI + the extension wrapper) is… not a great time. Locally, we ran into endless Rack-CORS errors. Trial and error sessions that felt like a personal war.

In hindsight, the much cleaner solution would have been to spin up a separate lightweight app just for the extension code, pointed at the main app as an API. Staying in one repo is a common and totally understandable constraint: less duplication, easier to keep things in sync. The tradeoff is exactly what we ran into: CORS complexity and two contexts fighting each other locally. Worth knowing before you commit to that architecture.

Auth: The Real Boss Fight

Okay, if CORS was annoying, auth was brutal.

The main app used Devise with cookie-based sessions. Totally normal for a Rails app only. Totally terrible when you’re trying to embed it inside a Chrome extension where there’s now another session in a different context.

Cookies + iframes + different origins = CHAOS. The sessions would conflict, auth state got confused between the main UI and the extension view, and debugging it felt like a nightmare.

The core issue: the extension iframe was loading the app from the same origin as the main app, so cookies were being shared, but in ways that weren’t predictable or clean. Having the extension live in a separate app/repo with its own auth flow would have made this much more manageable.

Assets: Because Why Not Add One More Thing

Once auth was (mostly) sorted, the assets decided to have a turn.

Icons and images weren’t rendering inside the iframe. The Rails asset pipeline had opinions about how assets were served, and those opinions didn’t align with being loaded in a sandboxed Chrome extension context. Some path adjustments and pipeline config tweaks later, it was working, but it was one of those things where you fix it and never want to look at it again.

The Chrome Web Store Setup

Here’s the practical stuff that I wish was better documented:

You need a Chrome Developer account: a Google account registered as a Chrome Extension developer.

The client owned this account, which made sense since they’d be the publisher. But the Chrome Extension developer account didn’t support collaborators. So deployments of staging versions had to go through the client’s access. That’s a workflow you want to sort out early, when you are a developer on a client project: nothing worse than needing to push a fix and waiting for someone else to log in or test changes for you. And make debugging an extra hard task.

Anyhow, in practice, this is the flow:

  1. Create a placeholder app in the Chrome Web Store to get a unique extension ID
  2. Hardcode that ID into your Rails config (initializers/environment config). It’s used to toggle the extension open and controls how the iframe is allowed to load
  3. Develop locally using sandbox/unpublished mode
  4. Rotate to the production extension package when publishing

What I’d Do Differently

  • Separate repo for the extension code from day one. Fewer CORS headaches, cleaner auth, easier to reason about.
  • Get a reference from the client early. When they finally showed me the competitor’s extension they wanted to replicate, I’d already gone down two other paths. Ask “is there an example you want to match?” in the first meeting.
  • Plan for auth early. If your main app uses cookie sessions, figure out the extension auth strategy before you write a line of extension code.
  • Test assets in the extension context early. Don’t assume the pipeline will just work cause it probably won’t.

TL;DR

Chrome extensions are powerful and actually not that hard in isolation. The complexity explodes when you’re wrapping an existing app with existing auth and trying to share code. Clean separation of concerns is your friend. CORS is your nemesis. And always ask the client if they have a reference they want to take inspiration from.

Building a Chrome extension and not sure where to start? Now we know exactly how to. Let’s chat.