Build a To-Do App with Node.js, Express, and Vanilla JavaScript
Computer Science
Build a To-Do App with Node.js, Express, and Vanilla JavaScript
Build a To-Do App with Node.js, Express, and Vanilla JavaScript
Turn your current HTML and early React experience into a small full-stack to-do app. You will keep the interface simple, add a Node.js and Express server, connect browser JavaScript to server routes, and finish with a runnable project that lets someone add, view, complete, and remove tasks.
What you learn by building this
- Create and run a small Node.js project with Express
- Serve an existing-style HTML, CSS, and JavaScript front end from an Express server
- Design and implement task routes using JSON request and response data
- Use vanilla JavaScript and fetch to read and update server data
- Represent task state consistently across the server and browser
- Handle invalid input and visible success or error feedback
Learning Journey
Shape the interface and task behavior
4 lessonsStart from the learner's demonstrated HTML and CSS foundation. Build a standalone browser version first so the project has a visible interface and predictable task behavior before introducing the server runtime.
Introduce the Express server
3 lessonsCross the runtime bridge in small steps: create the Node project, run one server, then serve the already-working front end. The learner sees the same project working through Express before learning API design.
Move task data behind the API
4 lessonsReplace the browser-only task array with a deliberately small in-memory server model. Each step adds one route and immediately gives the browser a more realistic source of truth.
Connect the browser to the server
4 lessonsUse the learner's working browser interactions as the bridge into asynchronous programming. Replace local mutations one at a time with fetch calls, preserving the interface while changing its data source.
Polish, verify, and present the project
4 lessonsTurn the working prototype into a reliable portfolio-sized project. The learner checks the important paths, improves the interface details, and prepares a concise demonstration of the finished app.
Public lesson
Plan the task screen
Lesson 3.1 — Give the task screen a clear structure
You already know how to organize a semantic HTML page. This time, you’ll apply that same thinking inside a React component. For now, the screen only needs to look like a to-do page; behavior comes later.
1. See what the app currently renders
Tasks
Start the React app using the command your project already uses, such as:
npm run dev
npm run dev
Open the local URL in your browser.
Tasks
Look at the current screen and identify where the main app component is defined. It is often one of these:
src/App.jsxsrc/App.jssrc/App.tsx
Keep the existing imports and component setup. You’ll replace the component’s returned markup.
2. Sketch the page before writing JSX
Your page needs these regions:
- A page heading
- A form for adding a task
- A region containing the current tasks
- Controls for each task
- A feedback area for messages such as “Task added”
Use semantic elements so the page structure is understandable even without styling:
<main>for the page’s primary content<header>for the title and short introduction<section>for the form and task list<form>for entering a task<ul>and<li>for the tasks<label>connected to the text input<button>for actions
Tasks
Before coding, decide what each section should be called. For example, the form section could be “Add a task” and the list section could be “Your tasks.”
3. Build the JSX structure
Replace only the returned markup in your main component. The comments below are places where your page-specific JSX is still needed.
<!-- tangeble:starter -->function App() {
return (
<main className="todo-page">
{/* Add a header with the page title and a short description. */}
<section aria-labelledby="add-task-heading">
{/* Add an h2 whose id matches aria-labelledby. */}
<form>
{/* Add a label connected to the task text input. */}
{/* Add a text input with a useful name and placeholder. */}
{/* Add a submit button. */}
</form>
</section>
<section aria-labelledby="task-list-heading">
{/* Add an h2 whose id matches aria-labelledby. */}
<ul>
{/* Add two sample li elements so the page is visibly populated. */}
{/* Each task should have a checkbox, task text, and delete button. */}
</ul>
</section>
{/* Add a feedback paragraph with an accessible live-region role. */}
</main>
);
}
export default App;
function App() {
return (
<main className="todo-page">
{/* Add a header with the page title and a short description. */}
<section aria-labelledby="add-task-heading">
{/* Add an h2 whose id matches aria-labelledby. */}
<form>
{/* Add a label connected to the task text input. */}
{/* Add a text input with a useful name and placeholder. */}
{/* Add a submit button. */}
</form>
</section>
<section aria-labelledby="task-list-heading">
{/* Add an h2 whose id matches aria-labelledby. */}
<ul>
{/* Add two sample li elements so the page is visibly populated. */}
{/* Each task should have a checkbox, task text, and delete button. */}
</ul>
</section>
{/* Add a feedback paragraph with an accessible live-region role. */}
</main>
);
}
export default App;
A few JSX details matter here:
- Use
className, notclass. - Use
htmlForon a<label>, and make it match the input’sid. - Give the form input a
name. - Give each button a clear
type, such assubmitorbutton. - A checkbox should have a label or text that makes its purpose clear.
Tasks
Do not add state or event handlers yet. Static sample tasks are enough for this lesson.
4. Add just enough styling to read the structure
Use the stylesheet already connected to your app. If it is src/App.css, add styles that make the regions and task controls easy to distinguish.
.todo-page {
/* Set a readable width and spacing for the page. */
}
.todo-page header {
/* Add spacing below the page introduction. */
}
.todo-page section {
/* Add separation between the form and task list regions. */
}
.todo-page ul {
/* Remove the default list indentation and add vertical spacing. */
}
.todo-page li {
/* Arrange the checkbox, text, and delete button clearly. */
}
.feedback {
/* Make feedback easy to notice without overpowering the tasks. */
}
.todo-page {
/* Set a readable width and spacing for the page. */
}
.todo-page header {
/* Add spacing below the page introduction. */
}
.todo-page section {
/* Add separation between the form and task list regions. */
}
.todo-page ul {
/* Remove the default list indentation and add vertical spacing. */
}
.todo-page li {
/* Arrange the checkbox, text, and delete button clearly. */
}
.feedback {
/* Make feedback easy to notice without overpowering the tasks. */
}
Tasks
Choose simple values that fit the rest of the project. The goal is not visual polish; it should be immediately obvious where the form, list, controls, and feedback are.
5. Check the result in the browser
Tasks
Refresh the page and verify all of the following:
- The page has one clear main heading.
- The form has a visible label, text input, and submit button.
- The task list has at least two visible tasks.
- Each task has a checkbox and a delete button.
- The form and task list have their own visible headings.
- A feedback message appears somewhere on the page, even if it is currently static.
- The layout remains understandable when the browser window is narrow.
Tasks
Then use the keyboard:
- Press
Tabrepeatedly. - Confirm that the input, submit button, checkboxes, and delete buttons receive focus.
- Check that the focus order follows the page from top to bottom.
- Press the spacebar on a checkbox and confirm that it can be toggled.
Find the bug
Something's wrong — can you spot it?
If a label does not focus its input when clicked, compare the label’s htmlFor value with the input’s id. They must match exactly.
6. Make one structural review
Explain it back
Put it in your own words
Open your component and ask:
- Could someone understand this page from the headings alone?
- Is the task collection represented as a list rather than unrelated paragraphs?
- Does every action use a button?
- Is the feedback area separate from the task content?
- Did any element receive a class only because it was convenient, when a semantic element would be clearer?
20 more characters
When the browser shows a clearly organized task page and keyboard navigation reaches every control, this screen structure is ready for the next lesson.
Course Outline
5 modules · 19 lessons
Shape the interface and task behavior
Introduce the Express server
Move task data behind the API
Connect the browser to the server
Polish, verify, and present the project
Learn by building your own version.
Remix this public project to open the workspace, follow the guided build, and let the AI mentor teach you through the work instead of doing it for you.