Works with Paperclip
How Freecodecamp Curriculum fits into a Paperclip company.
Freecodecamp Curriculum drops into any Paperclip agent that handles this kind of work. Assign it to a specialist inside a pre-configured PaperclipOrg company and the skill becomes available on every heartbeat — no prompt engineering, no tool wiring.
S
SaaS FactoryPaired
Pre-configured AI company — 18 agents, 18 skills, one-time purchase.
$27$59
Explore packSource file
SKILL.md693 linesExpandCollapse
---name: freecodecamp-curriculumdescription: Comprehensive guide for contributing to and working with freeCodeCamp's open-source codebase and curriculum platformtriggers: - help me contribute to freeCodeCamp - how do I add a curriculum challenge to freeCodeCamp - set up freeCodeCamp locally - how does freeCodeCamp's challenge system work - create a new freeCodeCamp certification - freeCodeCamp development environment setup - how to write freeCodeCamp challenge tests - freeCodeCamp codebase structure--- # freeCodeCamp Curriculum & Platform Development > Skill by [ara.so](https://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 ```bashgit clone https://github.com/<YOUR_USERNAME>/freeCodeCamp.gitcd freeCodeCamp``` ### 2. Install Dependencies ```bashpnpm install``` ### 3. Configure Environment ```bashcp sample.env .env``` Key `.env` variables to set: ```bash# MongoDBMONGOHQ_URL=mongodb://127.0.0.1:27017/freecodecamp # GitHub OAuth (create at github.com/settings/developers)GITHUB_ID=$GITHUB_OAUTH_CLIENT_IDGITHUB_SECRET=$GITHUB_OAUTH_CLIENT_SECRET # AuthJWT_SECRET=$YOUR_JWT_SECRETSESSION_SECRET=$YOUR_SESSION_SECRET # Email (optional for local dev)SENDGRID_API_KEY=$SENDGRID_API_KEY``` ### 4. Seed the Database ```bashpnpm run seed``` ### 5. Start Development Servers ```bash# Start everything (API + Client)pnpm run develop # Or start individually:pnpm run develop:api # Fastify API on :3000pnpm run develop:client # Gatsby on :8000``` --- ## Curriculum Challenge Structure Challenges are stored as YAML/Markdown files under `curriculum/challenges/`. ### Challenge File Format ```yaml# curriculum/challenges/english/02-javascript-algorithms-and-data-structures/basic-javascript/comment-your-javascript-code.md ---id: bd7123c8c441eddfaeb5bdef # unique MongoDB ObjectId-style stringtitle: Comment Your JavaScript CodechallengeType: 1 # 1=JS, 0=HTML, 2=JSX, 3=Vanilla JS, 5=Project, 7=VideoforumTopicId: 16783dashedName: 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 ```jsassert(code.match(/(\/\/)/).length > 0);``` hint 2 ```jsassert(code.match(/(\/\*[\s\S]+?\*\/)/).length > 0);``` # --seed-- ## --seed-contents-- ```js// Your starting code here``` # --solutions-- ```js// 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 interactivelypnpm run create-challenge # Or use the helper directlycd tools/challenge-helper-scriptspnpm 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 ```bash# Generate a unique challenge IDnode -e "const {ObjectID} = require('mongodb'); console.log(new ObjectID().toString())"``` 3. Follow the challenge file format above ### Validate Your Challenge ```bash# Lint and validate all curriculum filespnpm run test:curriculum # Test a specific challengepnpm run test:curriculum -- --challenge <challenge-id> # Test a specific blockpnpm run test:curriculum -- --block basic-javascript``` --- ## Writing Challenge Tests Tests use a custom assertion library. Inside `# --hints--` blocks: ### JavaScript Challenges ```markdown# --hints-- `myVariable` should be declared with `let`. ```jsassert.match(code, /let\s+myVariable/);``` The function should return `true` when passed `42`. ```jsassert.strictEqual(myFunction(42), true);``` The DOM should contain an element with id `main`. ```jsconst el = document.getElementById('main');assert.exists(el);`````` ### Available Test Utilities ```js// DOM access (for HTML challenges)document.querySelector('#my-id')document.getElementById('test') // Code inspectionassert.match(code, /regex/); // raw source code stringassert.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 objectconst result = await fetch('/api/test');assert.strictEqual(result.status, 200);``` --- ## API Development (Fastify) ### Route Structure ```typescript// api/src/routes/example.tsimport { 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 ```typescript// api/src/app.ts - register the pluginimport { exampleRoutes } from './routes/example'; await fastify.register(exampleRoutes, { prefix: '/api' });``` ### Database Access (Mongoose) ```typescript// api/src/schemas/user.tsimport 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 ```tsx// client/src/pages/my-new-page.tsximport 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 ```tsx// client/src/redux/selectors.ts patternimport { createSelector } from 'reselect';import { RootState } from './types'; export const userSelector = (state: RootState) => state.app.user; export const completedChallengesSelector = createSelector( userSelector, user => user?.completedChallenges ?? []);``` ```tsx// In a componentimport { useAppSelector } from '../redux/hooks';import { completedChallengesSelector } from '../redux/selectors'; const MyComponent = () => { const completedChallenges = useAppSelector(completedChallengesSelector); return <div>{completedChallenges.length} challenges completed</div>;};``` ### i18n Translations ```tsx// Add keys to client/i18n/locales/english/translations.json{ "my-component": { "title": "My Title", "description": "My description with {{variable}}" }} // Use in componentconst { t } = useTranslation();t('my-component.title');t('my-component.description', { variable: 'value' });``` --- ## Testing ### Unit Tests (Jest) ```bash# Run all unit testspnpm test # Run tests for a specific packagepnpm --filter api testpnpm --filter client test # Watch modepnpm --filter client test -- --watch``` ### Curriculum Tests ```bash# Validate all challengespnpm run test:curriculum # Validate specific superblockpnpm run test:curriculum -- --superblock javascript-algorithms-and-data-structures # Lint challenge markdownpnpm run lint:curriculum``` ### E2E Tests (Playwright) ```bash# Run all e2e testspnpm run test:e2e # Run specific test filepnpm run test:e2e -- e2e/learn.spec.ts # Run with UIpnpm run test:e2e -- --ui``` ### Writing E2E Tests ```typescript// e2e/my-feature.spec.tsimport { 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 ```bash# Developmentpnpm run develop # Start all servicespnpm run develop:api # API onlypnpm run develop:client # Client only # Buildingpnpm run build # Build everythingpnpm run build:api # Build APIpnpm run build:client # Build client (Gatsby) # Testingpnpm test # Unit testspnpm run test:curriculum # Validate curriculumpnpm run test:e2e # Playwright e2e # Lintingpnpm run lint # ESLint all packagespnpm run lint:curriculum # Curriculum markdown lint # Databasepnpm run seed # Seed DB with curriculum datapnpm run seed:certified-user # Seed a test certified user # Utilitiespnpm run create-challenge # Interactive challenge creatorpnpm 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: ```typescript// tools/challenge-helper-scripts/helpers/id-gen.tsimport { ObjectId } from 'bson';export const generateId = (): string => new ObjectId().toHexString();``` ### Adding Forum Links Every challenge needs a `forumTopicId` linking to forum.freecodecamp.org: ```yamlforumTopicId: 301090 # Must be a real forum post ID``` ### Curriculum Meta Files Each block needs a `_meta.json`: ```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 ```typescript// In e2e tests, use the test user fixtureimport { 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 ```bash# Check if MongoDB is runningmongosh --eval "db.adminCommand('ping')" # Start MongoDB (macOS with Homebrew)brew services start mongodb-community # Use in-memory MongoDB for testsMONGOHQ_URL=mongodb://127.0.0.1:27017/freecodecamp-test pnpm test``` ### Port Conflicts ```bash# API runs on 3000, Client on 8000lsof -i :3000kill -9 <PID>``` ### Curriculum Validation Failures ```bash# See detailed error outputpnpm run test:curriculum -- --verbose # Common issues:# - Missing forumTopicId# - Duplicate challenge IDs# - Invalid challengeType# - Malformed YAML frontmatter``` ### Node/pnpm Version Mismatch ```bash# Use the project's required versionsnode --version # Should match .nvmrcpnpm --version # Should match packageManager in package.json nvm use # Switches to correct Node version``` ### Client Build Errors ```bash# Clear Gatsby cachepnpm --filter client run cleanpnpm run develop:client``` --- ## Contributing Workflow ```bash# 1. Create a feature branchgit checkout -b fix/challenge-typo-in-basic-js # 2. Make changes and testpnpm run test:curriculumpnpm test # 3. Lintpnpm run lint # 4. Commit using conventional commitsgit commit -m "fix(curriculum): correct typo in basic-javascript challenge" # 5. Push and open PR against maingit push origin fix/challenge-typo-in-basic-js``` Commit message prefixes: `fix:`, `feat:`, `chore:`, `docs:`, `refactor:`, `test:` --- ## Resources - Contribution guide: https://contribute.freecodecamp.org- Forum: https://forum.freecodecamp.org- Discord: https://discord.gg/PRyKn3Vbay- How to report bugs: https://forum.freecodecamp.org/t/how-to-report-a-bug/19543Related skills
Agency Agents Ai Specialists
Install Agency Agents Ai Specialists skill for Claude Code from aradotso/trending-skills.
Agent Browser Automation
Install Agent Browser Automation skill for Claude Code from aradotso/trending-skills.
Antigravity Manager
Install Antigravity Manager skill for Claude Code from aradotso/trending-skills.