It’s been 2 months since my last blog post, and today I want to share some thoughts about plugin systems.
“It is our choices that show what we truly are, far more than our abilities.”
— Albus Dumbledore, Harry Potter and the Chamber of Secrets
About a month ago, I redesigned squarified
and discovered quite a few issues with my original architecture design. For instance, I had made all event handlers and highlight events private by default. But after using it in real projects, I realized that some features weren’t really what users needed. Maybe they just wanted to display the layout without any viewport interactions :)
One Size Doesn’t Fit All
Being opinionated in design might not always be the best approach. What if we could let users choose what they actually need? Just like LEGO blocks - you can freely combine pieces to build exactly what you want. This realization made me think that a plugin-based design might be the way to go.
Building Your Plugin System
As mentioned above, we should build a plugin system that gives users the freedom to choose what they need. I designed the plugin hooks based on real-world scenarios I encountered while working with squarified
. You can find more details in the Hook Lifecycle documentation.
The core idea is simple: instead of forcing users to accept all features, let them pick and choose through plugins. Think of it as a modular approach where each plugin handles a specific concern - whether it’s event handling, color mapping, or custom interactions.
Here’s how the plugin architecture works:
// Plugin definition is straightforward
const myPlugin = definePlugin({
name: 'my-custom-plugin',
onLoad(treemapContext, domEvent) {
// Initialize your plugin when the treemap loads
},
onDOMEventTriggered(name, event, module, domEvent) {
// Handle DOM events like clicks, hovers, etc.
},
onResize(domEvent) {
// Respond to viewport changes
}
})
// Using plugins is just as simple
const treemap = new Treemap({
plugins: [myPlugin]
})
The plugin system provides several lifecycle hooks that cover different scenarios:
- onLoad: Perfect for initialization logic
- onModuleInit: Great for customizing how modules are processed
- onDOMEventTriggered: Handle user interactions
- onResize: Respond to layout changes
- onDispose: Clean up when the treemap is destroyed
What I find particularly elegant about this approach is the PluginContext
- it gives plugins access to essential functionality without exposing the entire internal API. Plugins can resolve modules by ID, access metadata from other plugins, and interact with the main instance, but they can’t accidentally break the core functionality.
The cascadeHook
method is especially useful for plugins that need to contribute to a shared result, like color mappings. Multiple plugins can provide their own color schemes, and the system automatically merges them together.
This design philosophy of “composition over configuration” means users only pay for what they use, both in terms of bundle size and complexity.
What I Learned Along the Way
Honestly, building this plugin system was quite a journey, and I made plenty of mistakes along the way.
Less is More: I used to think that exposing more APIs would make plugins more powerful. Turns out, it just made everything harder to maintain. When I had to change some internal methods, half the plugins broke! Now I’m much more careful about what I expose publicly.
Always Have a Plan B: No matter how well you design your system, someone will always want to do something you never thought of. That’s why I added the PluginContext
- it’s like a “break glass in case of emergency” option. Sure, it’s not the prettiest solution, but sometimes you need an escape hatch.
Make Things Obvious: At first, my plugin lifecycle was all over the place. Plugins didn’t know when they were supposed to run, and I kept getting confused bug reports. Breaking everything down into clear phases (load → init → event → dispose) made life so much easier for everyone.
The funny thing is, most of these lessons came from users doing things I never expected. That’s the beauty of plugin systems - they reveal all the assumptions you didn’t know you were making.
Final Thoughts
Plugin systems aren’t just about technical architecture - they’re about empowering users to solve their own problems. When you give people the tools to extend your software, amazing things happen.
As Dumbledore said, it’s about choices. And good plugin systems give users better choices.
If you’re interested in building plugins for squarified, check out the plugin development guide. I’d love to see what you create!