freeCodeCamp.org is a friendly community where you can learn to code for free. It is run by a donor-supported 501(c)(3) nonprofit to help millions of busy adults transition into tech. Our community has already helped more than 40,000 people get their first developer job. Our full-stack web development and machine learning curriculum is completely free and self-paced. We have thousands of interactive coding challenges to help you expand your skills.
A self-hosted solution to help teachers plan and manage classroom-based learning, on top of freeCodeCamp's learning platform.
For a while now teachers have been asking for a way to get a birds eye view of multiple students who are progressing through the course. This is why we set out to make freeCodeCamp classroom mode, an interactive dashboard for teachers to view multiple freeCodeCamp users’ progress on their courses.
You can use GitHub Codespaces, a local dev container, or your own machine.
- Fork this repository.
- Open the fork in GitHub Codespaces, or open it in VS Code with "Dev Containers: Reopen in Container".
- Wait for the setup to complete. The container installs the dependencies, starts Postgres and Mailpit, and seeds the database.
- Start the app and the mock freeCodeCamp data server in two terminals:
pnpm run develop pnpm run mock-fcc-data
Prerequisites:
- Node.js 26. The version is in
.nvmrc. If you use fnm or nvm, runfnm useornvm usein the project folder. - pnpm 12. The version is in the
packageManagerfield ofpackage.json. To install it, runnpm install --global pnpm@12. - Docker with Docker Compose.
Steps:
-
Clone your fork and install the dependencies:
git clone https://github.com/<your-username>/classroom.git cd classroom pnpm install
-
Run the setup:
pnpm run setupThis command does these steps:
- It copies
.env.sampleto.envif.envdoes not exist. - It starts Postgres and Mailpit from
docker-compose.yml. - It applies the database migrations and seeds the database.
You can run it again at any time. It does not change an existing
.env. It resets the seed users and the seed classroom, and keeps all other data. - It copies
-
Start the app and the mock freeCodeCamp data server in two terminals:
pnpm run develop pnpm run mock-fcc-data
-
Open http://localhost:3001.
The app runs on port 3001 and the mock data on port 3002. These ports do not conflict with freeCodeCamp's main platform (ports 3000 and 8000).
You do not need an Auth0 account for local development. .env.sample sets FCC_ENABLE_DEV_LOGIN_MODE=true, so the sign in page shows a "Development sign in" form. Type an email and select the button to sign in as that user.
The seed creates these users:
| Role | |
|---|---|
admin@classroom.local |
Admin |
teacher@classroom.local |
Teacher |
student@classroom.local |
Student |
The seed also creates the classroom seed-classroom for the teacher. A new email creates a new user with no role.
The app refuses to start in production when FCC_ENABLE_DEV_LOGIN_MODE is true.
In development, the app sends email to Mailpit. Open http://localhost:8026 to read it. Mailpit uses ports 1026 and 8026, so it does not conflict with the Mailpit of freeCodeCamp's main platform (ports 1025 and 8025). Use this to test the teacher invitation flow.
pnpm run lint
pnpm testThe end-to-end tests use Playwright. Install the browser once:
pnpm run test:e2e:installThe end-to-end tests write to the database. Run them against a separate database, not the one that you use for development:
- Stop
pnpm run develop. If a server already runs on port 3001, the tests use that server and its database. - Create and seed a test database.
prisma migrate deploycreates the database if it does not exist:DATABASE_URL=postgresql://postgres:password@localhost:5432/classroom_e2e pnpm exec prisma migrate deploy DATABASE_URL=postgresql://postgres:password@localhost:5432/classroom_e2e pnpm exec prisma db seed
- Run the tests:
DATABASE_URL=postgresql://postgres:password@localhost:5432/classroom_e2e pnpm run test:e2e
The app connects to the database in DATABASE_URL. To use a managed Postgres database, set DATABASE_URL to its connection string, then apply the migrations:
pnpm exec prisma migrate deployDo not run pnpm run setup for a managed database, because it starts the local Postgres container.
We recommend styling components using our design style guide.
We are strongly opinionated about adding new variables/tokens to the colors. After careful research, the colors have been chosen to respect the freeCodeCamp brand identity, developer experience, and accessibility.
We recommend going through our system design diagram.
What it is: The challenge map (data/challengeMap.json) is a flat lookup
that maps every freeCodeCamp challenge id to the superblock(s) and block(s) it
belongs to, plus its human-readable name:
{
"<challengeId>": {
"superblocks": ["responsive-web-design", "responsive-web-design-22"],
"blocks": ["basic-html-and-html5"],
"name": "Say Hello to HTML Elements"
}
}Why Classroom needs it: FCC Proper reports a student's progress as a flat
list of completed challenge ids, with no curriculum structure attached. The
teacher dashboard needs that progress grouped by certification and block. The
helpers in util/challengeMapUtils.ts look each
completed id up in this map and re-nest the flat data into the
{ certifications: [...] } shape the dashboard renders. A challenge can belong
to several superblocks/blocks (e.g. current and legacy -22 versions), so both
are stored as arrays ordered as the GraphQL build encounters them. The dashboard
must group each challenge under exactly one cert/block, so
util/challengeMapHelpers.ts centralizes a
"first occurrence wins" rule: the first element of each array is used as the
canonical location. This keeps callers and tests from independently re-implementing
the same choice and drifting apart.
It is built from the FCC Proper GraphQL curriculum database by
scripts/build-challenge-map-graphql.ts.
To generate or refresh the map:
pnpm run challenge-mapTo run the challenge map tests (they read the current data/challengeMap.json):
pnpm run test:challenge-mapProduction uses Auth0, like freeCodeCamp's main platform. You need Auth0 only to test the real sign in flow.
-
Go to auth0.com and sign in.
-
Create a "Regular Web Application".
-
In the "Settings" tab, copy these values to your
.envfile:Auth0 setting .envvariableExample Domain AUTH0_DOMAINyour-tenant.us.auth0.comClient ID AUTH0_CLIENT_IDabc123Client Secret AUTH0_CLIENT_SECRETexample-secretWrite
AUTH0_DOMAINwithouthttps://and without a trailing slash. -
In the same tab, add these URLs, then save the changes:
Auth0 setting Value Allowed Callback URLs http://localhost:3001/api/auth/callback/auth0Allowed Logout URLs http://localhost:3001For GitHub Codespaces, use
https://{CODESPACE_NAME}-3001.app.github.devin place ofhttp://localhost:3001. Also setNEXTAUTH_URLandCLASSROOM_APP_BASE_URLin.envto this URL. Auth0 accepts more than one URL, separated by commas. -
Set
FCC_ENABLE_DEV_LOGIN_MODE=falsein.env, then restartpnpm run develop. The sign in page now sends you to Auth0.
To use one Auth0 application for both Classroom and freeCodeCamp's main platform on your machine, add both callback URLs:
| Auth0 setting | Value |
|---|---|
| Allowed Callback URLs | http://localhost:3000/auth/auth0/callback, http://localhost:3001/api/auth/callback/auth0 |
| Allowed Logout URLs | http://localhost:3000, http://localhost:3001 |
This video shows the setup.
If the sign in fails:
- "Callback URL mismatch": the Allowed Callback URLs do not include the exact URL of the app.
- "Invalid state": clear the cookies for
localhost, then try again. - Examine the Auth0 logs in the Auth0 dashboard for the error message.
The app refuses to start when DATABASE_URL, NEXTAUTH_URL or NEXTAUTH_SECRET is empty.
In production, the app also refuses to start when one of these conditions is true:
AUTH0_DOMAIN,AUTH0_CLIENT_IDorAUTH0_CLIENT_SECRETis empty.NEXTAUTH_SECREThas the sample value. Make a secret withopenssl rand -base64 32.FCC_ENABLE_DEV_LOGIN_MODEistrue.
Set the SMTP_* variables to your email provider. .env.sample lists all the variables.
When TEACHER_INVITES_ENABLED is true, an admin can invite a teacher from the admin dashboard:
- The admin creates an invitation, or sends an invitation again.
- Classroom sends an email with a link in the form
/teacher/invite/<token>.CLASSROOM_APP_BASE_URLsets the start of the link. - The teacher signs in with the invited email and accepts the invitation.
- Classroom gives the teacher the
TEACHERrole.
To test this flow locally, sign in as admin@classroom.local and invite an email. Read the invitation in Mailpit. Then open the link in a private browser window and sign in with the invited email.
Join us in our Discord Chat here.
freeCodeCamp uses the following terms:
Certification = 'superblock'
Course(s) = 'blocks'
Everything under a course/block is a 'challenge'
Copyright © 2021 freeCodeCamp.org
The content of this repository is bound by the following licenses:
- The computer software is licensed under the BSD-3-Clause license.

