Putnami
DocsGitHub

Licensed under FSL-1.1-MIT

Getting Started
Concepts
How To
Build A Web App
Build An Api Service
Share Code Between Projects
Configure Your App
Add Persistence
Add Authentication
Add Background Jobs
Principles
Tooling & Workspace
Workspace Overview
Cli
Jobs & Commands
SDK
Error Handling
Extensions
Typescript
Go
Python
Docker
Ci
Frameworks
Typescript
OverviewWebReact RoutingForms And ActionsStatic FilesApiErrors And ResponsesConfigurationLoggingHttp And MiddlewareDependency InjectionPlugins And LifecycleSessionsAuthPersistenceEventsStorageCachingWebsocketsTestingHealth ChecksTelemetryProto GrpcSmart Client
Go
OverviewHttpDependency InjectionPlugins And LifecycleConfigurationSecurityPersistenceErrorsEventsStorageCachingLoggingTelemetryGrpcService ClientsValidationOpenapiTesting
Platform
  1. DocsSeparator
  2. How ToSeparator
  3. Build A Web App

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. Run bunx putnami serve @sample/ts-react-app from the workspace root.

Steps

1) Create the app

putnami init --workspace my-workspace --project web
cd my-workspace

This 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 web

Open 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.ts

4) 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 with action.ts
  • See the components/counter.tsx for a client-side interactive component
  • Add persistence for database-backed data
  • Add authentication to protect routes

On this page

  • Build a web app
  • Steps
  • 1) Create the app
  • 2) Run it
  • 3) Explore the project structure
  • 4) Add a new page
  • 5) Load data from the server
  • Result
  • Next steps