> Documentation for Redweb 0.13.0. Install that exact version when following these examples.

# Render JSX without React

Build a two-page TypeScript site with shared navigation, a stylesheet and per-page metadata. JSX is a markup syntax here: Redweb renders it on Node.js, without React hooks, hydration, or a browser component runtime. This is useful for documentation, content sites and server-rendered pages that do not need browser-side component execution.

## Explain it like I'm five

The page class is a recipe and the server is the kitchen. `render()` prepares HTML before it reaches the browser. A shared layout adds the same navigation around each page, like putting different meals on the same kind of plate. The browser receives the finished document, not the kitchen.

## Follow the design

1. The initializer supplies `redweb/tsconfig.json` inheritance, TypeScript, the stylesheet and the entrypoint helper. Keep the file as `.tsx`; do not point its JSX settings at `react/jsx-runtime`.
2. `defineSite()` supplies one layout and CSS declaration. Its page decorators register `/` and `/about`, with metadata beside each page.
3. Each `render()` returns ordinary TSX. Function components can share presentation; page-specific data remains in your server code. Text and attribute values are escaped, and URL protocols are restricted.
4. `createApp()` combines both pages on one listener. The standalone entrypoint owns bounded shutdown through the shared helper; importing the module starts nothing.

Keep CSS in external files. The [rendering reference](/docs/reference/0.13.0/live-html.md) covers components, templates, assets and static export. For interactive pages, start from the [realtime counter](/docs/reference/0.13.0/recipes/realtime.md): assignments to decorated state update the browser through Redweb's runtime. Non-live site pages do not acquire that behavior just because their markup is JSX.

## Check that it works

Open `http://localhost:8181/`, follow About, and confirm the navigation and styling stay consistent while the title and content change. View the response source: it is server-rendered HTML, not an empty mount point. The [shipped test](/docs/reference/0.13.0/recipes/site/files/test/app.test.cjs) checks actual HTTP responses, both pages, CSS and absence of the live-page runtime. The package gate repeats it with the source directory unavailable.

`npm run build` produces compiled code and copied assets; `npm start` serves that output. `site.export()` is a separate static-export workflow, not what the starter's default build does. Static export cannot replace protected or live application requests.

## When to choose another approach

Redweb TSX is not React-compatible. Do not import React components or expect hooks, client effects, browser rendering or automatic support for browser-only libraries. Choose an appropriate browser framework when those are core requirements. Live Redweb applications require a Node host with long-lived listeners; exported static files have a different deployment model. See [compatibility and release verification](/docs/reference/0.13.0/release-trust.md).

## Build and run the complete application

```sh
npx --yes redweb@0.13.0 init my-site --template site
cd my-site
npm install --save-exact redweb@0.13.0
npm test
npm run dev
```

The [complete site recipe](/docs/reference/0.13.0/recipes/site.md) contains every generated file, its real acceptance tests, and deployment instructions. The source below is one of those files, not a standalone program; initialize the whole project before modifying it.

## Source walkthrough: src/app.tsx

```tsx
import { defineSite, start, type LiveHtmlStartOptions } from 'redweb';
import { runApp } from './run-app';

const site = defineSite({
    css: 'app.css',
    layout: content => <body><nav><a href="/">Home</a> · <a href="/about">About</a></nav>{content}</body>,
});

@site.page('/', { head: { title: 'My Redweb site', description: 'A server-rendered TypeScript site.' } })
export class HomePage {
    render() {
        return <main class="home"><h1>Your server-rendered app is ready.</h1><p>Edit src/app.tsx to make it yours.</p></main>;
    }
}

@site.page('/about', { head: { title: 'About' } })
export class AboutPage {
    render() { return <main class="home"><h1>About</h1><p>Shared layout, separate pages, no browser JavaScript.</p></main>; }
}

export function createApp(options: LiveHtmlStartOptions = {}) {
    return start([HomePage, AboutPage], { port: Number(process.env.PORT ?? 8181), templateRoot: __dirname, ...options });
}

if (require.main === module) runApp(createApp);
```
