MCPInstall

freecodecamp-curriculum

Comprehensive guide for contributing to and working with freeCodeCamp's open-source codebase and curriculum platform

Installs
1.4K
Last 24h
—
Trending rank
—
Security
Not audited

Install

Works with every agent the skills CLI supports.

npx skills add https://github.com/reason-machines/trending-skills --skill freecodecamp-curriculum
  1. 01

    Install

    Run the command in your project. The CLI asks which agents to install for.

  2. 02

    Describe the task

    Ask your agent for something this skill covers. It loads freecodecamp-curriculum automatically when relevant.

  3. 03

    Review the result

    The agent follows the SKILL.md steps, scripts and references bundled with the skill.

SKILL.md

The instructions your agent reads when the skill is triggered.

freeCodeCamp Curriculum & Platform Development

Skill by ara.so — Daily 2026 Skills collection.

freeCodeCamp.org is a free, open-source learning platform with thousands of interactive coding challenges, certifications, and a full-stack curriculum. The codebase includes a React/TypeScript frontend, Node.js/Fastify backend, and a YAML/Markdown-based curriculum system.


Architecture Overview

freeCodeCamp/
├── api/                   # Fastify API server (TypeScript)
├── client/                # Gatsby/React frontend (TypeScript)
├── curriculum/            # All challenges and certifications (YAML/Markdown)
│   └── challenges/
│       ├── english/
│       │   ├── responsive-web-design/
│       │   ├── javascript-algorithms-and-data-structures/
│       │   └── ...
│       └── ...
├── tools/
│   ├── challenge-helper-scripts/  # CLI tools for curriculum authoring
│   └── ui-components/             # Shared React components
├── config/                # Shared configuration
└── e2e/                   # Playwright end-to-end tests

Local Development Setup

Prerequisites

  • Node.js 20+ (use nvm or fnm)
  • pnpm 9+
  • MongoDB (local or Atlas)
  • A GitHub account (for OAuth)

1. Fork & Clone

git clone https://github.com/<YOUR_USERNAME>/freeCodeCamp.git
cd freeCodeCamp

2. Install Dependencies

pnpm install

3. Configure Environment

cp sample.env .env

Key .env variables to set:

# MongoDB
MONGOHQ_URL=mongodb://127.0.0.1:27017/freecodecamp

# GitHub OAuth (create at github.com/settings/developers)
GITHUB_ID=$GITHUB_OAUTH_CLIENT_ID
GITHUB_SECRET=$GITHUB_OAUTH_CLIENT_SECRET

# Auth
JWT_SECRET=$YOUR_JWT_SECRET
SESSION_SECRET=$YOUR_SESSION_SECRET

# Email (optional for local dev)
SENDGRID_API_KEY=$SENDGRID_API_KEY

4. Seed the Database

pnpm run seed

5. Start Development Servers

# Start everything (API + Client)
pnpm run develop

# Or start individually:
pnpm run develop:api      # Fastify API on :3000
pnpm run develop:client   # Gatsby on :8000

Curriculum Challenge Structure

Challenges are stored as YAML/Markdown files under curriculum/challenges/.

Challenge File Format

# curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/comment-your-javascript-code.md

---
id: bd7123c8c441eddfaeb5bdef  # unique MongoDB ObjectId-style string
title: Comment Your JavaScript Code
challengeType: 1              # 1=JS, 0=HTML, 2=JSX, 3=Vanilla JS, 5=Project, 7=Video
forumTopicId: 16783
dashedName: comment-your-javascript-code
---

# --description--

Comments are lines of code that JavaScript will intentionally ignore.

```js
// This is an in-line comment.
/* This is a multi-line comment */

--instructions--

Try creating one of each type of comment.

--hints--

hint 1

assert(code.match(/(\/\/)/).length > 0);

hint 2

assert(code.match(/(\/\*[\s\S]+?\*\/)/).length > 0);

--seed--

--seed-contents--

// Your starting code here

--solutions--

// inline comment
/* multi-line
   comment */

### Challenge Types

| Type | Value | Description |
|------|-------|-------------|
| HTML | 0 | HTML/CSS challenges |
| JavaScript | 1 | JS algorithm challenges |
| JSX | 2 | React component challenges |
| Vanilla JS | 3 | DOM manipulation |
| Python | 7 | Python challenges |
| Project | 5 | Certification projects |
| Video | 11 | Video-based lessons |

---

## Creating a New Challenge

### Using the Helper Script

```bash
# Create a new challenge interactively
pnpm run create-challenge

# Or use the helper directly
cd tools/challenge-helper-scripts
pnpm run create-challenge --superblock responsive-web-design --block css-flexbox

Manual Creation

  1. Find the correct directory under curriculum/challenges/english/
  2. Create a new .md file with a unique ID
# Generate a unique challenge ID
node -e "const {ObjectID} = require('mongodb'); console.log(new ObjectID().toString())"
  1. Follow the challenge file format above

Validate Your Challenge

# Lint and validate all curriculum files
pnpm run test:curriculum

# Test a specific challenge
pnpm run test:curriculum -- --challenge <challenge-id>

# Test a specific block
pnpm run test:curriculum -- --block basic-javascript

Writing Challenge Tests

Tests use a custom assertion library. Inside # --hints-- blocks:

JavaScript Challenges

# --hints--

`myVariable` should be declared with `let`.

```js
assert.match(code, /let\s+myVariable/);

The function should return true when passed 42.

assert.strictEqual(myFunction(42), true);

The DOM should contain an element with id main.

const el = document.getElementById('main');
assert.exists(el);

### Available Test Utilities

```js
// DOM access (for HTML challenges)
document.querySelector('#my-id')
document.getElementById('test')

// Code inspection
assert.match(code, /regex/);          // raw source code string
assert.include(code, 'someString');

// Value assertions (Chai-style)
assert.strictEqual(actual, expected);
assert.isTrue(value);
assert.exists(value);
assert.approximately(actual, expected, delta);

// For async challenges
// Use __helpers object
const result = await fetch('/api/test');
assert.strictEqual(result.status, 200);

API Development (Fastify)

Route Structure

// api/src/routes/example.ts
import { type FastifyPluginCallbackTypebox } from '../helpers/plugin-callback-typebox';
import { Type } from '@fastify/type-provider-typebox';

export const exampleRoutes: FastifyPluginCallbackTypebox = (
  fastify,
  _options,
  done
) => {
  fastify.get(
    '/example/:id',
    {
      schema: {
        params: Type.Object({
          id: Type.String()
        }),
        response: {
          200: Type.Object({
            data: Type.String()
          })
        }
      }
    },
    async (req, reply) => {
      const { id } = req.params;
      return reply.send({ data: `Result for ${id}` });
    }
  );

  done();
};

Adding a New API Route

// api/src/app.ts - register the plugin
import { exampleRoutes } from './routes/example';

await fastify.register(exampleRoutes, { prefix: '/api' });

Database Access (Mongoose)

// api/src/schemas/user.ts
import mongoose from 'mongoose';

const userSchema = new mongoose.Schema({
  email: { type: String, required: true, unique: true },
  completedChallenges: [
    {
      id: String,
      completedDate: Number,
      solution: String
    }
  ]
});

export const User = mongoose.model('User', userSchema);

Client (Gatsby/React) Development

Adding a New Page

// client/src/pages/my-new-page.tsx
import React from 'react';
import { Helmet } from 'react-helmet';
import { useTranslation } from 'react-i18next';

const MyNewPage = (): JSX.Element => {
  const { t } = useTranslation();

  return (
    <>
      <Helmet>
        <title>{t('page-title.my-new-page')} | freeCodeCamp.org</title>
      </Helmet>
      <main>
        <h1>{t('headings.my-new-page')}</h1>
      </main>
    </>
  );
};

export default MyNewPage;

Using the Redux Store

// client/src/redux/selectors.ts pattern
import { createSelector } from 'reselect';
import { RootState } from './types';

export const userSelector = (state: RootState) => state.app.user;

export const completedChallengesSelector = createSelector(
  userSelector,
  user => user?.completedChallenges ?? []
);
// In a component
import { useAppSelector } from '../redux/hooks';
import { completedChallengesSelector } from '../redux/selectors';

const MyComponent = () => {
  const completedChallenges = useAppSelector(completedChallengesSelector);
  return <div>{completedChallenges.length} challenges completed</div>;
};

i18n Translations

// Add keys to client/i18n/locales/english/translations.json
{
  "my-component": {
    "title": "My Title",
    "description": "My description with {{variable}}"
  }
}

// Use in component
const { t } = useTranslation();
t('my-component.title');
t('my-component.description', { variable: 'value' });

Testing

Unit Tests (Jest)

# Run all unit tests
pnpm test

# Run tests for a specific package
pnpm --filter api test
pnpm --filter client test

# Watch mode
pnpm --filter client test -- --watch

Curriculum Tests

# Validate all challenges
pnpm run test:curriculum

# Validate specific superblock
pnpm run test:curriculum -- --superblock javascript-algorithms-and-data-structures

# Lint challenge markdown
pnpm run lint:curriculum

E2E Tests (Playwright)

# Run all e2e tests
pnpm run test:e2e

# Run specific test file
pnpm run test:e2e -- e2e/learn.spec.ts

# Run with UI
pnpm run test:e2e -- --ui

Writing E2E Tests

// e2e/my-feature.spec.ts
import { test, expect } from '@playwright/test';

test('user can complete a challenge', async ({ page }) => {
  await page.goto('/learn/javascript-algorithms-and-data-structures/basic-javascript/comment-your-javascript-code');

  // Fill in the code editor
  await page.locator('.monaco-editor').click();
  await page.keyboard.type('// inline comment\n/* block comment */');

  // Run tests
  await page.getByRole('button', { name: /run the tests/i }).click();

  // Check results
  await expect(page.getByText('Tests Passed')).toBeVisible();
});

Key pnpm Scripts Reference

# Development
pnpm run develop              # Start all services
pnpm run develop:api          # API only
pnpm run develop:client       # Client only

# Building
pnpm run build                # Build everything
pnpm run build:api            # Build API
pnpm run build:client         # Build client (Gatsby)

# Testing
pnpm test                     # Unit tests
pnpm run test:curriculum      # Validate curriculum
pnpm run test:e2e             # Playwright e2e

# Linting
pnpm run lint                 # ESLint all packages
pnpm run lint:curriculum      # Curriculum markdown lint

# Database
pnpm run seed                 # Seed DB with curriculum data
pnpm run seed:certified-user  # Seed a test certified user

# Utilities
pnpm run create-challenge     # Interactive challenge creator
pnpm run clean                # Clean build artifacts

Superblock & Block Naming Conventions

Superblocks map to certifications. Directory names use kebab-case:

responsive-web-design/
javascript-algorithms-and-data-structures/
front-end-development-libraries/
data-visualization/
relational-database/
back-end-development-and-apis/
quality-assurance/
scientific-computing-with-python/
data-analysis-with-python/
machine-learning-with-python/
coding-interview-prep/
the-odin-project/
project-euler/

Block directories within a superblock:

responsive-web-design/
├── basic-html-and-html5/
├── basic-css/
├── applied-visual-design/
├── css-flexbox/
└── css-grid/

Common Patterns & Gotchas

Challenge ID Generation

Every challenge needs a unique 24-character hex ID:

// tools/challenge-helper-scripts/helpers/id-gen.ts
import { ObjectId } from 'bson';
export const generateId = (): string => new ObjectId().toHexString();

Adding Forum Links

Every challenge needs a forumTopicId linking to forum.freecodecamp.org:

forumTopicId: 301090  # Must be a real forum post ID

Curriculum Meta Files

Each block needs a _meta.json:

{
  "name": "Basic JavaScript",
  "dashedName": "basic-javascript",
  "order": 0,
  "time": "5 hours",
  "template": "",
  "required": [],
  "isUpcomingChange": false,
  "isBeta": false,
  "isLocked": false,
  "isPrivate": false
}

Testing with Authentication

// In e2e tests, use the test user fixture
import { authedUser } from './fixtures/authed-user';

test.use({ storageState: 'playwright/.auth/user.json' });

test('authenticated action', async ({ page }) => {
  // page is already logged in
  await page.goto('/settings');
  await expect(page.getByText('Account Settings')).toBeVisible();
});

Troubleshooting

MongoDB Connection Issues

# Check if MongoDB is running
mongosh --eval "db.adminCommand('ping')"

# Start MongoDB (macOS with Homebrew)
brew services start mongodb-community

# Use in-memory MongoDB for tests
MONGOHQ_URL=mongodb://127.0.0.1:27017/freecodecamp-test pnpm test

Port Conflicts

# API runs on 3000, Client on 8000
lsof -i :3000
kill -9 <PID>

Curriculum Validation Failures

# See detailed error output
pnpm run test:curriculum -- --verbose

# Common issues:
# - Missing forumTopicId
# - Duplicate challenge IDs
# - Invalid challengeType
# - Malformed YAML frontmatter

Node/pnpm Version Mismatch

# Use the project's required versions
node --version   # Should match .nvmrc
pnpm --version   # Should match packageManager in package.json

nvm use          # Switches to correct Node version

Client Build Errors

# Clear Gatsby cache
pnpm --filter client run clean
pnpm run develop:client

Contributing Workflow

# 1. Create a feature branch
git checkout -b fix/challenge-typo-in-basic-js

# 2. Make changes and test
pnpm run test:curriculum
pnpm test

# 3. Lint
pnpm run lint

# 4. Commit using conventional commits
git commit -m "fix(curriculum): correct typo in basic-javascript challenge"

# 5. Push and open PR against main
git push origin fix/challenge-typo-in-basic-js

Commit message prefixes: fix:, feat:, chore:, docs:, refactor:, test:


Resources

FAQ

What does the freecodecamp-curriculum skill do?

Comprehensive guide for contributing to and working with freeCodeCamp's open-source codebase and curriculum platform

How do I install freecodecamp-curriculum?

Run npx skills add https://github.com/reason-machines/trending-skills --skill freecodecamp-curriculum in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/freecodecamp-curriculum/ (personal) or .claude/skills/freecodecamp-curriculum/ (project).

Which AI agents can use freecodecamp-curriculum?

Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.

Is freecodecamp-curriculum safe to install?

No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.

Who maintains freecodecamp-curriculum?

It is published by reason-machines in reason-machines/trending-skills. It has 1,419 recorded installs.

More from reason-machines/trending-skills

View all →

openclaw-control-center

reason-machines/trending-skills

Local-first, security-first control center for OpenClaw agents — visibility dashboard with readonly defaults, token attribution, collaboration tracing, and safe write operations.

Data & Databases4.4K installs

inkos-multi-agent-novel-writing

reason-machines/trending-skills

Multi-agent CLI system for autonomous novel writing, auditing, and revision with human review gates

Docs & Writing2.8K installs

claude-hud-statusline

reason-machines/trending-skills

A Claude Code plugin that displays a real-time HUD showing context usage, active tools, running agents, and todo progress in your terminal statusline.

understand-anything-knowledge-graph

reason-machines/trending-skills

Turn any codebase into an interactive knowledge graph using Claude Code skills — explore, search, and ask questions about any project visually.

agency-agents-ai-specialists

reason-machines/trending-skills

A collection of specialized AI agent personalities for Claude Code, Cursor, Aider, Windsurf, and other AI coding tools — covering engineering, design, marketing, sales, and more.

AI & Agents2.1K installs

agent-browser-automation

reason-machines/trending-skills

Headless browser automation CLI for AI agents using native Rust binary with Chrome DevTools Protocol

Popular code quality & workflow skills

View category →

grill-me

mattpocock/skills

A relentless interview to sharpen a plan or design.

Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.

handoff

mattpocock/skills

Compact the current conversation into a handoff document for another agent to pick up.

domain-modeling

mattpocock/skills

Build and sharpen a project's domain model. Use when discussing codebase terminology, writing or editing a GLOSSARY.md, or recording or editing an ADR.

codebase-design

mattpocock/skills

Shared vocabulary for designing deep modules. Use when the user wants to design or improve a module's interface, find deepening opportunities, decide where a seam goes, make code more testable or AI-navigable, or when another skill needs the deep-module vocabulary.

diagnosing-bugs

mattpocock/skills

Diagnosis loop for hard bugs and performance regressions. Use when the user says "diagnose"/"debug this", or reports something broken/throwing/failing/slow.