3. Pages and routes
In the default main.wasp.ts file created by wasp new, there is a page and a route spec:
- JavaScript
- TypeScript
import { app, page, route } from "@wasp/spec"
import { MainPage } from "./src/MainPage" with { type: "ref" }
export default app({
// ...
spec: [
// We specify that the React implementation of the page is exported from
// `src/MainPage.jsx`. Reference imports must point to files inside `src`.
route("RootRoute", "/", page(MainPage)),
],
})
import { app, page, route } from "@wasp/spec"
import { MainPage } from "./src/MainPage" with { type: "ref" }
export default app({
// ...
spec: [
// We specify that the React implementation of the page is exported from
// `src/MainPage.tsx`. Reference imports must point to files inside `src`.
route("RootRoute", "/", page(MainPage)),
],
})
Together, these specifications tell Wasp that when a user navigates to /, it should render the MainPage component from src/MainPage.tsx.
The MainPage Component
Let's take a look at the React component referenced by the page spec:
- JavaScript
- TypeScript
import Logo from "./assets/wasp-logo-rounded.svg";
import "./Main.css";
export function MainPage() {
// ...
}
import Logo from "./assets/wasp-logo-rounded.svg";
import "./Main.css";
export function MainPage() {
// ...
}
This is a regular functional React component. It also imports some CSS and a logo from the assets folder.
That is all the code you need! Wasp takes care of everything else necessary to define, build, and run the web app.
wasp start automatically picks up the changes you make, regenerates the code, and restarts the app. So keep it running in the background.
It also improves your experience by tracking the working directory and ensuring the generated code/types are up to date with your changes.
If you are using TypeScript, your editor may sometimes report type and import errors even while wasp start is running.
This happens when the TypeScript Language Server gets out of sync with the current code. If you're using VS Code, you can manually restart the language server by opening the command palette and selecting "TypeScript: Restart TS Server." Open the command palette with:
Ctrl+Shift+Pif you're on Windows or Linux.Cmd+Shift+Pif you're on a Mac.
Adding a Second Page
To add more pages, you can add another route to your spec. You can even add parameters to the URL path, using dynamic segments. Let's test this out by adding a new page:
import { app, page, route } from "@wasp/spec"
import { HelloPage } from "./src/HelloPage" with { type: "ref" }
export default app({
// ...
spec: [
route("HelloRoute", "/hello/:name", page(HelloPage)),
],
})
When a user visits /hello/their-name, Wasp renders the component exported from src/HelloPage.tsx and you can use the useParams hook from react-router to access the name parameter:
- JavaScript
- TypeScript
import { useParams } from "react-router";
export const HelloPage = () => {
const { name } = useParams();
return <div>Here's {name}!</div>;
};
import { useParams } from "react-router";
export const HelloPage = () => {
const { name } = useParams<"name">();
return <div>Here's {name}!</div>;
};
Now you can visit /hello/johnny and see "Here's johnny!"
Since you are using TypeScript, you can benefit from using Wasp's type-safe Link component and the routes object. Check out Navigating between pages for more details.
Cleaning Up
Now that you've seen how Wasp deals with Routes and Pages, it's time to build something useful: a full-stack Todo app where users sign up, log in, and manage their own tasks stored in a database.
Start by cleaning up the starter project and removing unnecessary code and files.
The starter already comes with the base styles for the Todo app. You only need to add the styles for the task list and its form. You don't need to understand them to follow the tutorial. They only make the app look nice.
Add these styles to the end of src/Main.css:
/* ... existing styles */
input {
border-style: solid;
}
input[type="checkbox"] {
width: 1.25rem;
height: 1.25rem;
margin: 0;
accent-color: var(--yellow);
}
button,
input[type="submit"] {
padding: 0.75rem 1.25rem;
border: var(--border);
background: var(--yellow);
color: var(--black);
font: inherit;
font-weight: 700;
cursor: pointer;
}
button:hover,
input[type="submit"]:hover {
background: var(--yellow-dark);
}
header button {
padding: 0.5rem 1rem;
}
.new-task-form {
display: flex;
gap: 0.5rem;
}
.new-task-form input[type="text"] {
flex: 1;
min-width: 0;
padding: 0.75rem 1rem;
border: var(--border);
background: var(--white);
font: inherit;
}
.tasks {
margin: 0;
padding: 0;
border: var(--border);
background: var(--white);
box-shadow: 6px 6px 0 var(--yellow);
list-style: none;
}
.tasks li + li {
border-top: var(--border);
}
.tasks label {
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem 1rem;
cursor: pointer;
}
.tasks label:has(input:checked) {
opacity: 0.6;
text-decoration: line-through;
}
Then, replace the MainPage component with the skeleton of the Todo app, a header with the app title and an empty main area:
- JavaScript
- TypeScript
import Logo from "./assets/wasp-logo-rounded.svg";
import "./Main.css";
export const MainPage = () => {
return (
<>
<header>
<h1>
<img src={Logo} alt="Wasp logo" />
Todo App
</h1>
</header>
<main>Hello world!</main>
</>
);
};
import Logo from "./assets/wasp-logo-rounded.svg";
import "./Main.css";
export const MainPage = () => {
return (
<>
<header>
<h1>
<img src={Logo} alt="Wasp logo" />
Todo App
</h1>
</header>
<main>Hello world!</main>
</>
);
};
At this point, the main page should look like this:
You can now delete src/HelloPage.tsx (we won't need this page for the rest of the tutorial).
Since src/HelloPage.tsx no longer exists, remove its route from the main.wasp.ts file.
Your Wasp file should now look like this:
import { app, page, route } from "@wasp/spec"
import { MainPage } from "./src/MainPage" with { type: "ref" }
export default app({
name: "TodoApp",
wasp: { version: "^0.26" },
title: "TodoApp",
head: ["<link rel='icon' href='/favicon.ico' />"],
spec: [
route("RootRoute", "/", page(MainPage)),
],
})
Excellent work!
You now have a basic understanding of Wasp and are ready to start building your TodoApp. We'll implement the app's core features in the following sections.