Build a web app
You will create a React web app with Putnami, explore its structure, and add a new page with server-side data loading.
Companion sample:
samples/ts/03-react-app— a runnable project covering pages, layouts, loaders, actions, and forms. Runbunx putnami serve @sample/ts-react-appfrom the workspace root.
Steps
1) Create the app
putnami init --workspace my-workspace --project web
cd my-workspaceThis scaffolds a full React SSR application from the typescript-web template, with file-based routing, a layout, pages, and a client-side counter component.
2) Run it
putnami serve webOpen http://localhost:3000. You see the welcome page with navigation to About and Guestbook pages.
3) Explore the project structure
The scaffolded project lives in packages/web/:
packages/web/
├── src/
│ ├── main.ts # Application entrypoint
│ ├── serve.ts # Server bootstrap
│ ├── app/
│ │ ├── layout.tsx # Root layout (nav, theme toggle)
│ │ ├── page.tsx # Home page
│ │ ├── about/page.tsx # About page
│ │ ├── guestbook/
│ │ │ ├── page.tsx # Guestbook page
│ │ │ ├── loader.ts # Server-side data loader
│ │ │ └── action.ts # Form action handler
│ │ ├── error.tsx # Error boundary
│ │ └── not-found.tsx # 404 page
│ └── components/
│ └── counter.tsx # Client-side interactive component
└── test/
└── app.test.ts4) Add a new page
Create packages/web/src/app/hello/page.tsx:
import { page } from '@putnami/react';
import { Heading, Text } from '@putnami/ui';
export default page().render(function HelloPage() {
return (
<>
<Heading level={1}>Hello</Heading>
<Text as='p' color='secondary'>This is a new page.</Text>
</>
);
});Navigate to http://localhost:3000/hello. File-based routing picks it up automatically.
5) Load data from the server
Create packages/web/src/app/hello/loader.ts:
import { loader } from '@putnami/react';
export default loader(async () => {
return { message: 'Hello from the server', timestamp: new Date().toISOString() };
});Update packages/web/src/app/hello/page.tsx to use the loaded data:
import { page, useLoaderData } from '@putnami/react';
import { Heading, Text } from '@putnami/ui';
export default page().render(function HelloPage() {
const { message, timestamp } = useLoaderData<{ message: string; timestamp: string }>();
return (
<>
<Heading level={1}>{message}</Heading>
<Text as='p' color='secondary'>Loaded at {timestamp}</Text>
</>
);
});The loader runs on the server before rendering. The data is typed and available via useLoaderData.
Result
You have a React SSR app with layout, file-based routing, and server-side data loading. The template gave you a working starting point — you extended it with a new page and a loader.
Next steps
- Check the existing
guestbook/page for an example of form actions withaction.ts - See the
components/counter.tsxfor a client-side interactive component - Add persistence for database-backed data
- Add authentication to protect routes