Routes and data
Your models know how to read and write every table. But there's no page calling them yet. Before you build one, you need to understand how a route file connects your database to the browser.
Three exports, one file
In your portfolio site, route files exported a React component — that was the page. A database-backed route can export up to three things:
loader— runs on the server before the page renders. It reads data from the database and passes it to the component.export default— the React component. It receives the loader's data and renders what the user sees.action— runs on the server when the user submits a form. It writes data to the database and responds — redirect on success, errors on failure.
The flow goes: loader fetches data → component renders it → user interacts → action handles the submission → loader runs again with fresh data.
React Router doesn't care what you name the export default component. In
Gista.js, we always name it Page (or Layout for layout files) so
the role is obvious at a glance.

One file, both sides
The loader and action run on the server — they talk to the database and never ship to the browser. The component is what the user sees. They're all in the same file, but the framework keeps the boundaries straight.
In older web development, you'd have a backend route file (handling the database), a template file (rendering HTML), and a frontend script (handling interactions) — three files for one page. React Router collapses that into one place. When you want to understand how a page works, you open one file.
Reading vs. writing
A loader is for reading. When someone visits /forms/1, the loader loads that form from the database and hands it to the component. The page renders with the data already there.
An action is for writing. When someone fills out a form and clicks Save, the action receives the data, validates it, calls the model, and responds. Reading and writing happen in the same file, but they're triggered by different events — a page visit vs. a form submission.
You'll use both in the next step when you build the form creator. The action saves the form, the loader reads it back.
?What is a server action?Same page, different data
Notice the URL in that loader example — /forms/1. If you created another form, it would live at /forms/2 — same page, different data. There isn't a separate file for each form. There's one page template that works for any form ID.
This is called a dynamic route. The $id in the filename is a placeholder — the app reads the real ID from the URL, loads that form's data, and renders the right fields.