First off, thank you for considering contributing to SkilleTreeOSS! Our mission is the Democratization of Mastery, and we can only achieve that by crowdsourcing the best learning paths from people who have actually walked them.
You do not need to be a programmer to contribute to this project. We welcome two types of contributions:
- Content: Adding new skill trees or updating broken resource links (No coding required!).
- Code: Improving the Next.js/React Flow frontend or Supabase backend.
The fastest way to create or update a skill tree is the in-app builder at /builder. No JSON editing or GitHub knowledge required.
- Go to
/builder - Click Tree Metadata (top-left panel) and fill in the title, category, difficulty, description, icon, and estimated months
- Add nodes — double-click the canvas, press
Nat the cursor, or click+in the left toolbar - Connect nodes — drag from the bottom handle of one node to the top handle of another (or left→right in LR layout)
- Edit a node — click it to open the right editor panel; set label, description, zone, icon, and learning resources
- Multi-select — drag a rectangle in Select mode, or
Ctrl+clickto add nodes to the selection - Auto-layout — press
Ctrl+Lor the toolbar button to toggle between Top-Bottom and Left-Right Dagre layout - Click Preview in the header to see the tree exactly as learners will see it (all 4 themes, sidebar, prereq timeline)
- Click Submit PR to open a Pull Request:
- Anonymously — the SkillTreeOSS bot opens the PR on your behalf
- As your GitHub account — connect GitHub first (Settings → Connect GitHub)
Go to /builder/[treeId] — the tree is loaded from the published JSON and converted into builder nodes automatically.
| Shortcut | Action |
|---|---|
V |
Select tool |
H |
Pan tool |
N |
Add node at cursor position |
Double-click canvas |
Add node at that point |
Ctrl+A |
Select all nodes |
Ctrl+L |
Toggle layout direction (LR ↔ TB) |
Delete / Backspace |
Delete selected node(s) |
Ctrl+Z / Ctrl+Y |
Undo / Redo |
? |
Open shortcuts & guide modal |
Escape |
Deselect / close panels |
Press ? in the builder for the full interactive guide.
All the skill trees on the website are generated from simple text files formatted in JSON. They live in the data/trees/ folder of this repository.
To add a new skill tree or update an existing one, you just need to edit these files!
A skill tree file (e.g., urban-sketching.json) has three main parts: Metadata, Nodes (the skills), and Edges (the lines connecting them).
Here is a simple template:
{
"treeId": "urban-sketching",
"title": "Urban Sketching",
"category": "Art",
"difficulty": "easy",
"description": "Learn to draw the world around you on location.",
"version": "1.0",
"estimatedMonths": 2,
"totalNodes": 2,
"icon": "brush",
"nodes": [
{
"id": "perspective-101",
"label": "1-Point Perspective",
"description": "Understand the horizon line and vanishing points.",
"icon": "straighten",
"zone": "Foundation",
"resources": [
{
"id": "perspective-yt-artfundamentals",
"title": "One Point Perspective Drawing Tutorial",
"url": "https://youtube.com/watch?v=...",
"type": "video",
"author": "ArtFundamentals",
"estimatedHours": 1.5,
"isFree": true
}
],
"requires": []
},
{
"id": "ink-wash",
"label": "Ink & Wash Basics",
"description": "Adding depth with watercolor over ink.",
"icon": "water_drop",
"zone": "Technique",
"resources": [
{
"id": "ink-wash-yt-sketchingdaily",
"title": "Beginner Ink and Wash",
"url": "https://youtube.com/watch?v=...",
"type": "video",
"author": "SketchingDaily",
"estimatedHours": 2,
"isFree": true
}
],
"requires": ["perspective-101"]
}
],
"edges": [
{ "id": "e-perspective-ink", "source": "perspective-101", "target": "ink-wash" }
]
}- No coordinates needed: The app uses the Dagre layout engine to automatically compute node positions from the graph structure. You only need to define the
requiresrelationships — the visual tree arranges itself. Do not addpositionfields. difficultyvalues: Use"easy","medium", or"hard"(lowercase).- Edges:
sourceis the prerequisite node ID;targetis the node it unlocks. Every edge must have a correspondingrequiresentry on the target node. - Resources: Link only to 100% free resources unless
"isFree": falseis set. Link directly to the specific video or article — no generic landing pages or paid courses. Each node can have multiple resources (video + article + interactive). totalNodes: Must equal the number of entries in thenodesarray exactly.treeId: Kebab-case (^[a-z0-9-]+$); must match the JSON filename without the extension.
- Fork this repository.
- Create a new branch (e.g.,
add-urban-sketching-tree). - Add your
.jsonfile to thedata/trees/folder. - Submit a Pull Request — automated CI will validate your JSON against
data/schema.json.
If you want to help build the platform's UI or backend, we are thrilled to have you! We use Next.js 16, ReactFlow, Zustand, Tailwind CSS v4, Framer Motion, and Supabase.
- Fork and clone the repo.
- Run
npm install. - Duplicate
.env.local.exampleto.env.localand fill in your Supabase credentials (or use the mock keys from the example file for local UI development). - Run
npm run dev.
Open http://localhost:3000 for the main app and http://localhost:3000/builder for the builder.
| Path | Purpose |
|---|---|
app/(canvas)/ |
Canvas layout group — tree viewer and builder pages |
app/(canvas)/tree/[slug]/ |
Skill tree viewer page |
app/(canvas)/builder/ |
New tree builder page |
app/(canvas)/builder/[id]/ |
Edit existing tree page |
components/canvas/ |
Viewer canvas components (SkillCanvas, CustomNode, NodeSidebar, CanvasFAB) |
components/builder/ |
Builder components (BuilderCanvas, BuilderNode, BuilderNodeEditor, BuilderHeader, LeftToolbar, ContextMenu, ShortcutsModal, MetadataPanel, ResourceEditor, IconPicker, ZoneSelector) |
components/layout/ |
Navbar, Footer, UserMenu |
lib/store.ts |
Zustand store — viewer/learner state |
lib/builder-store.ts |
Zustand store — builder state (separate from viewer) |
lib/builder-utils.ts |
treeToDraft converter, BuilderDraft type |
lib/autoLayout.ts |
Dagre auto-layout (used by both viewer and builder) |
lib/utils.ts |
Pure utilities: getNodeStatus, getProgressPercent, formatHours, getLevelInfo |
data/trees/ |
Skill tree JSON content files |
data/schema.json |
JSON Schema (draft-07) for CI validation |
supabase/migrations/ |
SQL migration files |
types/ |
Shared TypeScript types (tree.ts, user.ts) |
- State Management — viewer: Use
lib/store.ts(useSkillTreeStore) for anything in the viewer canvas (completed nodes, selected node, canvas view, XP). Never use React Context for canvas state — it causes severe performance issues with ReactFlow. - State Management — builder: Use
lib/builder-store.ts(useBuilderStore) for all builder state. The two stores are completely independent; do not read from one in the other. - Builder node placement: When adding a node programmatically, call
findFreePosition(cx, cy, existingNodes)fromBuilderCanvas.tsxto pick the first unoccupied spiral-grid slot. This prevents nodes from stacking. - Builder viewport centering: Use
setViewport({ x: screenCX - nodeCX * zoom, y: screenCY - nodeCY * zoom, zoom })— notsetCenter— so the fixed-position editor panel is accounted for. ReadpanelWfromdocument.querySelector('[data-builder-panel]').offsetWidth. autoLayout.ts: CallcomputeAutoLayout(tree, view, dir, dims?)— pass{ w: 224, h: 180 }asdimsfrom the builder so Dagre uses the actual builder node size rather than per-view defaults.- XP / Levels:
XP_PER_NODE,LEVEL_THRESHOLDS, andgetLevelInfo()are exported fromlib/utils.ts. Import from there rather than redefining locally. - Styling: Use Tailwind utility classes. Theme tokens are defined in
app/globals.css(background-dark=#0f0f0f,surface-dark=#1a1a1a,card-dark=#1a2920,primary=#11d452). - Animations: Use Framer Motion
layoutIdfor element-position animations between shared elements (builder tool bubble, header tab bubble). UseAnimatePresencefor mount/unmount transitions. - Database (Supabase): If your PR requires schema changes, add a migration file to
supabase/migrations/(20240104_…format) and describe it in your PR. Always enable RLS on new tables. - Server Components & Cookies: Use
createServerSupabaseClient()fromlib/supabase-server.tsin Server Components and Route Handlers. - Featured Trees: Edit
lib/featured-trees.tsonly — no other code changes needed.
- Ensure your code type-checks (
npx tsc --noEmit), lints (npm run lint), and builds (npm run build). - Update
CHANGELOG.mdwith a brief description of your change under[Unreleased]. - Update
README.mdif you've added new environment variables, routes, or major features. - Your PR will be reviewed by a maintainer within a few days.
Welcome to the party. Let's build the ultimate map of human knowledge!
