Growing up, I spent my spare time doing what most programmers did: played video games every waking moment. I loved Adventure games and what a time sink they were. If time was the Mary Rose, and I was the French, my artillery were games like Kingdom Hearts, Ōkami, and Borderlands.

Why did I, and others, spend so much of our spare time exploring, surviving, dying, and (so, so much) grinding? Hundreds of factors contribute toward making an engaging experience, but the one I’m going to focus on is the notion of progression.

The idea of gamification isn’t new. Many popular applications (like todoist, or challenge timer) have incorporated some sort of progression scheme to make us, the consumers, use their app, give them money, and hand over our personal data. So I decided to go about my way of enabling others to do just that, through beautiful skill trees! Note: I expect neither money nor data from those using my skill trees.

The last few weeks have seen me toil away to create what I hope to be a pleasant plug’n’play React package to help you create exciting skill trees. You can test it yourself by following the tutorial. I hope it will be a frictionless experience.

We hope to have something resembling the skill tree below:

“A dying puppy. A baby in tears. If the previous statements elicited any emotional reaction report to your supervisor for summary destruction.”

[email protected]

Grab the starter repo by using git clone [email protected]:andrico1234/borderlands-skill-tree.git

Move into the directory and run the starting script, yarn start. Give the site a whirl, you'll see nothing but the Borderlands logo and environment.

beautiful-skill-tree exposes three components: the SkillProvider, SkillTreeGroup, and the SkillTree components.

SkillProvider: This takes in no props and supplies the children with the skill tree's context. This puppy handles all of the global data related to the skill tree.

SkillTreeGroup: Is more involved in that it can take an optional theme property, where we can pass in some custom styling, to make our skill tree feel very Borderlands. The SkillTreeGroup also uses the children-as-a-function pattern to give us access to some imperative api functionality, such as skill tree reset, selected skills counter, etc. We don't need to worry about any of those for the scope of this article.

SkillTree: This is the most exciting of the package's exports, unless you're a sucker for typings (which are also exported, for all you TS fans). The SkillTree doesn't take any children but requires 3 props: treeId, title, and data. The treeId should be an id that's unique to each skill tree, but should be persistent across user sessions as this is used as the key for getting and setting the data to local storage. I'm not going to explain what the title prop does, I'll leave you to experiment. The data is the mixing pot of the application. You'll pass in your skill tree data structure which the app will use to render a beautiful-skill-tree. Let's get a real basic tree going before we move on to our multi-tree, multi-branch Borderlands spectacular.

In App.tsx, import the 3 components like so:

import { SkillProvider, SkillTreeGroup, SkillTree } from 'beautiful-skill-tree';

Place it underneath your img tag, outside of the image's container div, but within the outer div. Add the SkillProvider, passing the SkillTreeGroup as a child. Before you do the same with the SkillTree, remember that as SkillTreeGroup uses function-as-a-child pattern, you'll need to render a function that returns the child components. Return a single SkillTree and give it a treeId and a title prop. Pass an empty array into the data prop so your App.tsx looks like this:

function App() {
  return (
    <div>
    // <div headercontent />
      <SkillProvider>
        <SkillTreeGroup>
          {() => {
            return (
              <SkillTree treeId="basic-birch" title="First Skill Tree" data={[]} />
            )
          }}
        </SkillTreeGroup>
      </SkillProvider>
    </div>
  );
}

Go to localhost:3000 to see the application running. You should see the logo, background, and a grey rectangle. If you’re running into any errors, go through the introduction again and check to see if there any syntax error or incorrect imports.

Next, let’s create a real basic tree. Just 3 items that move in a linear line. The data structure for data looks like this:

type Skill = {
  id: string;
  icon?: string;
  title: string;
  tooltip: {
    description : string;
  },
  children: Skill[];
}

Each skill requires four properties, with one being optional. You should also notice that the children property is a recursive type, meaning that it takes an array of the same data structure, which it uses to render the children of the skill. This can go on infinitely, and make for some real complicated, winding trees. I'll create the first skill for you, and I'll trust you with carrying on for the next two items.

const data = [{
  id: 'first-skill',
  title: 'The root node',
  tooltip: {
    description : "The parent node, all of the descendants will be locked until it's selected",
  },
  children: [
  // rinse and repeat; always repeat.
]}

Add the above snippet to the App.tsx file, and replace the empty array inside of the SkillTree's data property with our data definition. Load your page, and you should have an interactive node. Give it a hover and a click and it should be reacting to your actions. If things are working, then I'll task you with creating two (or more) child nodes. Experiment with children and sibling lengths, to see what you can come up with. (If you also happen to break my precious package, leave me a GitHub issue so I can patch things up).

Once you’re comfortable with creating a skill tree, let’s go ahead and create our Borderlands skill tree. Fortunately, I’ve done all of the tedious work for you and have already created the data structures and accumulated the images.

You’ll need to import the three trees from the data file, which can be done via

import { motion, harmony, cataclysm } from "./data/data";

The next step is creating two additional SkillTrees alongside the current one. You'll need to wrap them in a React.Fragment as your SkillTreeGroup will now be trying to render 3 top level components. Pass in the data accordingly, and if you're unsure, I've posted the code snippet below.

<React.Fragment>
  <SkillTree treeId="motion" title="Motion" data={motion} />
  <SkillTree treeId="harmony" title="Harmony" data={harmony} />
  <SkillTree treeId="cataclysm" title="Cataclysm" data={cataclysm} />
</React.Fragment>

Go ahead and check your web browser, it should be aaallmoost ready. We’ve got the skills rendered, but the styling feels a little lackluster. It doesn’t feel very Borderlands. Fortunately for you, I’m a regular Neil Buchanan and prepared a custom theme. Import the theme and pass it through to the SkillTreeGroup's theme prop. The theme object is export via import theme from './data/theme';. Easy!

“I HAVE ONE QUESTION FOR YOU. EXPLOSIONS?”

Once you’ve done the above, check out the finished product. If you’re still not satisfied with the styles, check out the theme object and customise it yourself, there are a bunch of additional attributes whose styles can be adjusted, so just peek into the typings of the package.

I mentioned earlier that there are a few additional properties and values that can be used to tweak the skill tree, so have a mess around yourself, and link me to any cool trees you create. I’d love to add it to the growing list of trees found here. Here’s an example of the skill tree that kickstarted this obsession.

I hope you’ve enjoyed tinkering with the beautiful-skill-tree package. I'm always adding new features and updating, so give it a star on github! You can find an online demo of the borderlands skill tree here

You can find me on Instagram or GitHub if you want to chat code, music, or fitness!