Powering Chrome Extensions with Backbone.js

Backbone.js has become one of the most popular JavaScript frameworks for structuring complex single-page applications. With its emphasis on maintaining data models and collections, manipulating views, and responding to events, Backbone encourages modular code that can scale across large projects.

But what about using Backbone beyond the confines of a traditional web app? Can its architectural patterns be applied effectively to other environments?

In this post, we‘ll explore integrating Backbone into Chrome extensions specifically. As we‘ll see, the framework pairs surprisingly well with extension development, facilitating everything from persistent data storage to UI rendering and logic flow.

An Intro to Backbone

For those less familiar, Backbone provides a client-side MVC-style structure for web projects. It aims to simplify coordination between the data (the "model") layer and the user interface (the "view") layer.

Some key capabilities include:

Models – used to represent domain data like a user, document, etc. Models handle syncing with APIs and storage.

Collections – ordered sets of models, analogous to arrays. Help manage lists of data.

Views – display visual representations of models and listen to changes.

Events – allow different components to communicate changes via events.

By separating concerns in this way, Backbone makes it easy to manage complexity as apps grow. Need to change a view without affecting models? No problem. New API data format? Just update the model.

Backbone also plays nicely with other libraries like Underscore/Lodash for utility functions and jQuery for DOM manipulation. This flexibility is a major contributor to its popularity.

Overall, if you have logically distinct data and presentation layers, Backbone handles the connection between them elegantly.

Chrome Extension Basics

Chrome extensions serve as mini add-on apps that extend the browser‘s capabilities. At their simplest, extensions inject code (JavaScript/CSS/assets) into either specific sites or all sites visited.

Some defining parts of extensions include:

Manifest – defines basic metadata like name, version, permissions, etc.

Content/Background Scripts – JavaScript that runs in the context of pages or an invisible background context.

HTML/CSS – UI code for popups, options pages that extensions may create.

APIs – access Chrome APIs like tabs, storage, messaging and more.

Extensions range from simple page modifications to complex apps with dedicated UIs. Under the hood, they leverage web development skills with added extension-specific APIs.

Now that we‘ve covered some Backbone and extension fundamentals, where do they intersect and how can Backbone bolster extension engineering?

Managing Data in Extensions with Backbone Models

One major aspect of many extensions is needing to work with data, whether user-provided through forms, persistent settings, data from web APIs, or content scraped from visited pages.

Backbone models provide an excellent way to encapsulate data within extensions:

// Example user model
var User = Backbone.Model.extend({

    // Default attributes 
    defaults: {
        name: "Guest",
        id: null
    },

    // Initialize logic
    initialize: function() {
       this.on("change:name", function() {
           this.saveUserToStorage();  
       });
    }

    // Persist to chrome.storage
    saveUserToStorage: function() {
        chrome.storage.sync.set({
            user: this.toJSON()
        });
    }
});

// Instantiate 
var user = new User();
user.set("name", "John"); // Automatically persists to storage

By centralizing data logic into models, we gain:

Validation – Enforce formats, types, values

Change Events – React to data changes

Persistent Storage – Sync to extension storage APIs

Encapsulation – Settings, user data all in one place

The structure keeps data modular as extensions scale. Models can be extended to add domain-specific features while handling cross-cutting tasks under the hood.

Maintaining Collections of Data

In addition to individual models, Backbone collections provide a handy way to coordinate lists of data:

var BookmarkList = Backbone.Collection.extend({
    model: Bookmark, // Referenced model

    localStorage: new Backbone.LocalStorage("bookmarks"),

    completed: function() {
        return this.filter(function(bookmark){
          return bookmark.get("completed");
        });
    }
});

var bookmarks = new BookmarkList();

bookmarks.fetch(); // Load from storage

bookmarks.completed(); // Get completed ones

With collections we centralize management around:

Retrieval/Save – Persist across extension reloads

Search/Filter – Find models based on attributes

Sort/Order – Control ordering

Syncing – Push/pull changes from server

For many types of extensions dealing with sets of items – bookmarks, history entries, settings – collections provide the desired coordination.

Structuring UI Code with Backbone Views

If your extension has any UI rendered via HTML/CSS/JavaScript, Backbone views can greatly assist with rendering logic.

Views map 1:1 with UI elements and handle events from user interactions.

var SettingsView = Backbone.View.extend({

    el: $(‘#settings‘), // DOM element to manage

    events: {
       "click .save": "saveSettings"   
    },

    saveSettings: function() {
        // Collect data and validate
        this.model.save(); 
    },

    render: function() {
        // Render view UI
        this.$el.html(this.template(this.model.toJSON());    
    }

});

By coupling views directly to UI pieces, we get:

Decoupled Logic – Views handle display separately from data

Declarative Event Binding – No need to manually attach event handlers

Compartmentalization – Individual UI pieces can be updated independently

Views make it simple to connect UI interactions to specific parts of the extension responsively.

Glueing It All Together with Events

A key advantage of Backbone is how its various components communicate using custom events:

// User logs in
userModel.set("name", "Nancy");

// Settings view sees event and updates
userModel.on("change:name", () => {
   settingsView.render(); 
});

finest details of the models and views don‘t need coordination. Changes propagate across the extension automatically via events.

Backbone events facilitate loose coupling where modules only care about specific notifications rather than micromanaging other components.

Additional Advantages

Beyond the central model-view-collection pieces, Backbone provides additional boons:

Routing – Organize different extension screens/pages for larger apps

Compatibility – Use with jQuery, Underscore, libraries like React

Structure – Extension code follows consistent conventions and contracts

Support – Huge community provides oversight for questions/issues

Testing – Components lend themselves well to isolated unit testing

The ecosystem around Backbone makes it even more versatile.

Chrome-Specific Considerations

While Backbone pairs nicely with extensions generally, there are some specific integration details worth noting:

Persistent Storage – Use chrome.storage instead of Backbone‘s built-in localStorage to sync data across extension reloads.

Cross-script – Communicate across background, content, and popup scripts via message passing.

Reload Behavior – Popups and content scripts reload often losing state like models. Store them in the background script.

External APIs – Augment models with Chrome APIs like tabs, bookmarks, etc. Lots of potential integrations.

Distribution – Bundle scripts with Webpack vs hosting Backbone itself externally.

Also be sure to register appropriate permissions for storage, tabs, etc. based on what extension capabilities require.

While Backbone won‘t solve all Chrome extension problems automatically, considering the patterns during development can pay dividends.

Sample Backbone Extension

To demonstrate Backbone capabilities more concretely within an extension, let‘s review a sample bookmark manager:

Gist Link for Full Source Code

Functionality

  • Save bookmarks to an online service
  • Tag and search bookmarks
  • View recently visited bookmarks
  • View summary statistics

Chrome API Integrations

  • bookmarks
  • storage
  • runtime

Backbone Components

  • Bookmark model
  • Bookmarks collection
  • Background coordinator
  • Popup views
  • Content script views

Key snippets:

Models Encapsulate Bookmarks

var Bookmark = Backbone.Model.extend({

    defaults: {
        title: "New Bookmark"    
    },

    // Additional domain logic
    // Tags, URL validation etc.

});

Collections Handle List Coordination

var Bookmarks= Backbone.Collection.extend({

    model: Bookmark,

    localStorage: new Backbone.LocalStorage("bookmarks-app")

}); 

Background Script Persists State

// Background coordinator
chrome.runtime.onInstalled.addListener(function() {

    window.bookmarks = new Bookmarks();

    // Load initial bookmarks   
    bookmarks.fetch();

});

Views Render UI

var PopupView = Backbone.View.extend({

    el: $(‘#app‘),

    initialize: function() {
        this.listenTo(bookmarks, "add", this.render); 
    },

    events: {
        "click .add": "promptNewBookmark"
    },

    promptNewBookmark: function() {
       // Show input for new bookmark  
       // Add via ‘bookmarks‘ collection
    },

    render: function() {
       var json = bookmarks.toJSON();
       this.$el.html(this.template(json));        
    }

});

Content Scripts Inject Logic

chrome.runtime.sendMessage({greeting: "hello"}, function(response) {

  // Access background page state
  window.bookmarks = response.bookmarks;

  // Show bookmark icons on tabs
  new TabDetectView();

});

While contrived, this demonstrates how Backbone components lend themselves well to extension capabilities.

Full source has additional functionality and integrations.

External Backbone Integration Examples

In addition to our sample, a few examples of open-source Chrome extensions built on Backbone include:

Backbone Bookmarker – Bookmarking app with analytics

Chromebug – Debugging companion utility

Wolfram Alpha Plus – Wolfram Alpha calculator tool

Youtube Enhancer – Adds features to YouTube video page

And there are many more ranging from developer tools to productivity enhancers to game aids.

The ecosystem continues to grow as the advantages Backbone confers become more well known.

Expert Wisdom on Architecture Best Practices

While a full deep dive on optimized Backbone extension architecture is outside our current scope, a few pro tips can set your efforts in the right direction:

  • Lazy load scripts – Only load JS as needed instead of all upfront
  • Minify sources – Reduce network payload size with tools like Webpack
  • API-fy access – Don‘t allow direct access to collections from views
  • Compartmentalize – Isolate components to ease testing and modifications
  • Plan permissions – Reduce attack surface by only requesting necessary extension access
  • Consider caching strategies – Store expensive data, limit unnecessary API calls

Following extension-specific best practices along with Backbone‘s conventions will ensure stability and scalability.

Conclusion

In closing, while Backbone.js originated to bring sanity to expansive web applications, many of the same data modeling and event-driven patterns apply equally well to the world of Chrome extensions.

Whether needing to coordinate settings and options, connect UI flows across popups and content scripts, or manage collections of persistent data, Backbone has you covered. Itsmaturity and popularity in the JavaScript ecosystem make it a battle-tested choice for extension developers.

So the next time you embark on writing a new packed extension app, consider making Backbone the architectural backbone powering your solution!

How useful was this post?

Click on a star to rate it!

Average rating 0 / 5. Vote count: 0

No votes so far! Be the first to rate this post.

Similar Posts