A React-based template for creating student-facing curriculum workshop site with interactive exercises, quizzes, and content management.
First-time setup for a project that depends on nss-core:
- Create and set your NPM token (
NPM_TOKEN) as described here
-
Create environment variables: Create a
.env.localfile in the project root use .env.template as a base and fill in the values:VITE_LEARNING_PLATFORM_API=http://localhost:8000If authentication is enabled (see Configuration section), also add:
VITE_OAUTH_CLIENT_ID=your_oauth_client_id VITE_PROXY_DOMAIN=your_proxy_domain -
Install dependencies:
npm install
-
Start the development server:
npm run dev
The application will be available at http://localhost:5173/[course-name]/ where [course-name] is derived from your courseName configuration.
This repository serves as a template for creating new workshop curriculum sites. Here's how to get started:
Edit src/config.js to customize your course:
const config = {
oauthClientId: import.meta.env.VITE_OAUTH_CLIENT_ID,
proxyDomain: import.meta.env.VITE_PROXY_DOMAIN,
baseUrl: import.meta.env.BASE_URL,
learningPlatformApi: import.meta.env.VITE_LEARNING_PLATFORM_API,
courseName: "Your Course Name Here", // Change this
doAuth: false, // Set to true if you need authentication
};-
courseName(string): The display name of your course. Used as the HTML page title and throughout the application. Automatically converted to URL-friendly format (lowercase, spaces to hyphens). -
doAuth(boolean):true: Enables authentication features, requires OAuth environment variablesfalse: Disables authentication for simpler deployment
When you create a new repository from this template, GitHub copies all workflow files
(in .github/workflows/) but does not copy secrets or workflow permissions.
Follow these steps to get GitHub Actions fully working.
- Go to your repository on GitHub.
- Click the Actions tab (top navigation bar).
- If you see a yellow banner saying “Workflows aren’t being run on this fork/template yet,”
click “I understand my workflows, enable them.” - If you see “Enable GitHub Actions” — click it to activate workflows.
Your repo is now allowed to run GitHub Actions.
- Go to Settings → Actions → General
- Scroll to Workflow permissions
- Choose “Read and write permissions”
- Check ✅ “Allow GitHub Actions to create and approve pull requests”
This template uses a modular, self-contained architecture where each section and chapter manages its own configuration:
src/
├── config.js # Global application configuration
├── sections/
│ ├── index.js # Main sections aggregator
│ ├── section-name/
│ │ ├── index.js # Section configuration and chapter imports
│ │ ├── 01-chapter-name/
│ │ │ ├── index.js # Chapter configuration
│ │ │ └── index.md # Chapter content (markdown)
│ │ ├── 02-another-chapter/
│ │ │ ├── index.js # Chapter configuration
│ │ │ └── index.md # Chapter content (markdown)
│ │ └── ...
│ └── another-section/
│ ├── index.js # Section configuration
│ └── ...
- Main Aggregator (
src/sections/index.js): Imports all sections viaimport.meta.globand combines their chapters into arrays for the application. You don't edit this to add content — new sections/chapters are picked up automatically. - Section Configuration (e.g.,
src/sections/01-getting-started/index.js): Defines section metadata and imports/orders all chapters in that section. - Chapter Configuration (e.g.,
src/sections/01-getting-started/01-class-setup/index.js): Defines chapter metadata, navigation, and imports the chapter's markdown content.
This walks through adding a new section titled "Deploying to Github" with two chapters: Overview and Github Actions.
Step 1: Create the section directory and its config file
mkdir src/sections/deploy-to-githubCreate src/sections/deploy-to-github/index.js:
// Import all chapter packages. This is a Vite feature.
const chapterModules = import.meta.glob('./*/index.js', { eager: true })
// Section configuration. This is the only code you change.
const config = {
id: "deploy-to-github",
title: "Deploying to Github",
description: "Deploying your project to the Github platform",
order: 90, // sections render lowest order first
required: true, // or required: false, optional: true for elective sections
}
const chapters = Object.values(chapterModules).map(chapter => ({ ...chapter.default, sectionId: config.id }))
export { chapters, config }| Option | Type | Description |
|---|---|---|
| id | string | A project-unique string slug identifier for this section |
| title | string | The value for the label in the left navigation bar |
| description | string | A brief description |
| order | int | Used to order sections vertically in the left nav, lowest first |
| required / optional | boolean | Marks the section as required work or optional/additional work in the nav |
Step 2: Create each chapter's directory and its two files
mkdir src/sections/deploy-to-github/01-overview
touch src/sections/deploy-to-github/01-overview/index.js
touch src/sections/deploy-to-github/01-overview/index.mdOpen src/sections/deploy-to-github/01-overview/index.js:
import content from "./index.md?raw";
export default {
id: "overview",
title: "Overview of Github Deployments",
previousChapterId: null,
nextChapterId: "github-actions",
exercise: null,
content
}| Option | Type | Description |
|---|---|---|
| id | string | A project-unique string slug identifier for this chapter |
| title | string | The value for the main header at the top of the content |
| previousChapterId | string | null | The id of the previous chapter, needed for the Previous Chapter button |
| nextChapterId | string | null | The id of the next chapter, needed for the Next Chapter button |
| exercise | object | null | Optional exercise data for the chapter; null for a plain reading chapter |
| content | string | The markdown content for the chapter, imported with ?raw |
Then write the actual lesson content in index.md:
Initial content for under the main header
## Subheadings as needed
More content...📝 The
titleproperty automatically becomes the mainH1element at the top of the content.
Step 3: Repeat for each remaining chapter
Numeric prefixes on chapter directories (01-, 02-) are just for readability when browsing the file tree — they aren't read by the app. Ordering within a section comes from the previousChapterId / nextChapterId chain.
- Section directories: numeric prefix + kebab-case (e.g.,
01-getting-started,02-llms-and-prompting) - Chapter directories: numeric prefix + kebab-case (e.g.,
01-class-setup,02-tokens-and-the-context-window) - Section/Chapter IDs: kebab-case, project-unique (chapter ids in this repo are prefixed with their section id, e.g.
llms-and-prompting-the-prediction-robot, so they stay unique across the whole course)
The template supports these environment variables:
Required:
VITE_LEARNING_PLATFORM_API: API endpoint for the learning platform
Optional (when doAuth: true):
VITE_OAUTH_CLIENT_ID: OAuth client ID for authenticationVITE_PROXY_DOMAIN: Domain for OAuth proxy
The course name in src/config.js automatically configures:
- Base path in
vite.config.js - GitHub Pages deployment paths
- Internal routing
Example: "Introduction to React" becomes /introduction-to-react/
- Fork/clone this template repository
- Update
src/config.jswith your course details - Create a section directory under
src/sections/for each module, with its ownindex.js(see "Creating Course Content" above) - Create a chapter directory inside each section for every lesson, each with its own
index.js+index.md - Test locally with
npm run dev - Deploy to your hosting platform
For questions about the NSS Core platform or this template, refer to the NSS Workshops Platform documentation.
This template is developed by Nashville Software School to provide free, accessible curriculum development tools for educators.