Towards the middle of last year I was introduced to virtual-dom, a standalone virtual-dom implementation inspired by React. After playing with a few examples the ideas that they presented started to really resonate.

As I began learning I quickly saw that there weren’t many virtual-dom resources out there. It just wasn’t popular enough.

But, since virtual-dom was inspired by the much more popular React, I was able to look up some existing React literature and have the ideas transfer right back to virtual-dom-land. For example, when I wanted to learn how to do inline styling in virtual-dom I simply googled how to do it in React.

I was excited to be able to use virtual-dom while still leveraging some of the work from the React community.

Unfortunately, that wasn’t exactly how things have played out.

Searching for components

After I got comfortable with virtual-dom I was ready to take advantage of one of its biggest offerings - easy to build and re-usable view components.

As you might imagine, the less popular virtual-dom didn’t have an ecosystem of open source components waiting for me. But, I thought that I’d surely be able to tap into the scores of React packages and simply replace their virtual-dom with my own.

It turned out that it wasn’t that easy. Every open source component that I found was coupled to React. More often than not this coupling was completely unnecessary, but even in cases where a coupling might have been helpful there was no underlying de-coupled API for me to leverage.

I’d see many of these components and think:

Why can’t I use this? This doesn’t even have anything to do with React. Mommy, daddy - it’s not fair!

At the time I wrote it off to the ecosystem being young. Everyone, including myself, was learning about how re-usable components should look. I imagined that over time we’d start settling into view components that were not just re-usable by your fellow library or framework users, but by others as well.

Fast forward to a year and a half later and this still isn’t the case.

This post aims to address the often unnecessary coupling of view components to their frameworks by first discussing the importance of framework agnostic components, and then by providing an short example implementation.

Why write framework agnostic view components?

Frameworks and libraries are no more than a means to an end. They help us with implementing the philosophies and patterns that we feel are the best practices at the time. They evolve in a world of trade-offs and constraints. As new tools are written and our machines change, the constraints of yesterday might not exist tomorrow. Inevitably, new frameworks and libraries pop up with hopes of better addressing a set of constraints.

Objectively, React has the most buzz right now, but history suggests that that will eventually change.

In the future, when React’s older and cooler sister comes to town, do we want to just rm -rf all of the old components that we’ve written? No. We don’t.

In the ideal case, we want it to be trivial to use today’s components five years from now without needing to port them.

Framework agnostic components also help us in our present day. Writing framework agnostic components allows more people to use your work. This helps ideas spread more quickly and reduces wheel reinventing.

We want to couple our work to battle tested idealogies and patterns, not our framework or library of choice!

Writing framework agnostic view components

Making your component framework agnostic isn’t hard. Your component will look almost exactly the same.

All you need to do is make sure that your component doesn’t know about your framework.

More specifically:

Write your component without having your framework or library be a dependency or peer dependency

How NOT to write your component

// Don't do this!
var React = require('react')

// Notice that React is a peer dependency
//  that our consumer has little to no control over
function myComponent (state) {
  return React.createElement('button', {
    id: 'cool-button'
  }, 'Click Me!')

In the code snippet above we’re explicitly depending on React. If someone comes across our library they’ll need to fork it and make it return something that their code understands.

Or, more realistically, they’ll throw their hands in the air and just write their own implementation - using up time that might have been spent innovating elsewhere.

How you SHOULD write your component

// Do this!

// Notice that we've given the user the ability to pass in
//  the DOM building function
function myComponent (someDOMCreator, state) {
  return someDOMCreator('button', {
    id: 'way-cooler-button'
  }, 'Click me!')

In the code snippet above we’re passing in a DOM builder that conforms to the hyperscript API. This could be React.createElement, virtual-dom/h or some custom function that wraps another framework such as choo.

It’s important to recognize that our component has absolutely no knowledge of which DOM builder it’s using. All it knows about is a simple API.

Keep in mind that this is only a rule of thumb. If your requirements make this the wrong idea, then try to at least make your framework specific code no more than a light wrapper around a separate framework-agnostic API.

For a better picture you can take a look at virtual-loading-dots and virtual-progress-bar - two slightly less trivial but still very simple examples of framework-agnostic view components.

Where to go from here

We want to write components that the JavaScript community can leverage for years to come. In five years there will be new frameworks and libraries that build on top of today’s ideas. We want our “re-usable” components to live up to their name and remain useful as today’s tooling evolves into tomorrow’s.